blehm/Duane Blehm's Code/Animation ƒ/Animation.Pas

535 lines
19 KiB
Plaintext
Executable File

Program Anim;
{this is a simple animation program.. in CreatePictures we load several
pictures. These pictures can be created in any Paint or Drawing type
program.. then copied to Scrapbook DA. Using ResEdit, the resource editor,
these Pictures can be copied from the Scrapbook into 'PICT' type resources.
Note that in our Anim.R source file we have an INCLUDE Anim.rsrc... when
RMaker compiles the Anim.R file it 'includes' any resources in the file
Anim.rsrc. That's how our Paint pictures get into the SimpleAnimation
resources.
Once we have them loaded into our various picHandles we then
CreateOffScreenBitMap so we have a place to DrawPicture() them. The
OffScreen bitmap is a section of memory just like the memory being
displayed on the screen... we just can't see it! at least not until we
CopyBits() some of it over to the visible screen Bitmap. We 'Draw' into
the OffScreen by setting our window^.portbits to our OffScreen bitmap..
watch out for 'clipping' if your OffScreen is bigger than your window.
Now for the fun! We have everything we need for the CopyBits() ROM call.
We have two Rectangles... one (sRect) that frames the shape to be copied
from OffScreen, the other (dRect) frames the location in our window where
we want the shape to be drawn. If dRect is not same size as sRect then
CopyBits will scale the shape to fit. This program uses the srcCopy mode
to 'erase' any previous stuff in the dRect... by using a 3 pixel white
border on the left side of our wagon we can offset it by 3 pixels per
animation loop and still 'erase' the previous wagon just by drawing over it.
Internal Animation (wheels turning, horse running,etc.) is achieved by
alternately drawing 3 different shapes in sequence as the dRect moves across
the screen... this is easily done by having an array of offscreen sRect's
and an index to step thru the array...}
{note: to move a given rectangle 'myRect' from its present location
(Current) to another location (Destination) the following can be used...
OffsetRect(myRect,Destination.h - Current.h,Destination.v - Current.v);
or...
OffsetRect(myRect,DestRect.left - myRect.left,DestRect.top - myRect.top);}
USES MacIntf;
{$L Anim.rsrc}{link resources...just our menu stuff}
CONST
lastMenu = 2; {Number of Menus}
appleMenu = 1;
fileMenu = 256;
WagonOne = 128;{Three wagon shapes loaded from resource file}
WagonTwo = 129;
WagonThree = 130;
{Resource Picts pasted from Scrapbook into Resource file using ResEdit}
VAR {global program stuff}
myDialog: DialogPtr;
myMenus: Array[1..lastMenu] of MenuHandle;
refNum,theMenu,
theItem: integer;
Finished: Boolean;{used to terminate the program}
ClockCursor: CursHandle; {handle to watch cursor}
myWindow: WindowPtr;
Screen,DragArea,
GrowArea: Rect;
HomeTown: PicHandle;{handle for our Logo pic}
HomeRect: Rect;{destination rect for our Logo}
Wagon: Array[1..3] of PicHandle;{we got three wagon pics}
WagonRect: Rect;{size of one wagon, onScreen destination for Copybits}
OffWagonRect: Array[1..3] of Rect;{wagon shape rects in offScreen}
WagonNdx: integer;{which of 3 wagon shapes is to be drawn}
{here's all the fly/regions stuff}
Fly: array[1..2] of PicHandle;{for loading 2 fly pictures}
FlyRect: Rect;{destination rect for drawing fly}
OffFly: array[1..2] of Rect;{source rects in offscreen}
FlyNdx: integer;{which offFly to draw}
Flower: PicHandle;{load picture from resource}
FlowerRect: Rect;{for locating the flower}
FlowerRgn: RgnHandle;{For 'clipping' fly and flower}
FlyLimits: Rect;{for fly border}
FlightRect: Rect;{For limiting fly flight}
CursorIsOn: Boolean;
MouseRect: Rect;{size of FlightRect, slides with mouse to smooth movement}
OffScreen,OldBits: BitMap; {for drawing into offscreen}
SizeOfOff: Size; {Size offscreen bitmap}
OffRowBytes: Integer;
{----------------------------------------------}
procedure CreatePictures; {get 3 Wagon PICT's from resource file}
var
i:integer;
Begin
HomeTown := GetPicture(131);{HomeTown logo}
HomeRect := HomeTown^^.picFrame;{size dest.Rest for drawing pic}
{we'll draw logo into upper right corner of window so relocate}
OffSetRect(HomeRect,myWindow^.portRect.right - 20 -
HomeRect.right,20 - HomeRect.top);
Wagon[1] := GetPicture(WagonOne); {load Wagon shapes from resources}
Wagon[2] := GetPicture(WagonTwo);
Wagon[3] := GetPicture(WagonThree);
WagonRect := Wagon[1]^^.picFrame; { i-159, size our WagonRect }
{Size Rects for OffScreen shapes...will locate them later}
For i := 1 to 3 do OffWagonRect[i] := WagonRect;
{load flystuff to demonstrate regions}
Fly[1] := GetPicture(132);
Fly[2] := GetPicture(133);
{size the fly rectangles}
For i := 1 to 2 do OffFly[i] := Fly[i]^^.picFrame;{they're both same size}
FlyRect := OffFly[1];
Flower := GetPicture(134);
FlowerRect := Flower^^.picFrame;
{load the region resource and coerce it to RgnHandle}
FlowerRgn := RgnHandle(GetResource('RGN ',128));{flower region... outline}
{note this region created with our 'Regions' utility}
end;
procedure CreateOffScreenBitMap; {see CopyBits stuff,also tech.note 41}
const
OffLeft = 0;
OffTop = 0;
OffRight = 115;
OffBottom = 95; {size bitmap to contain three wagon Picts}
var
bRect: Rect;
Begin
{find size/rows/bounds of bitimage}
SetRect(bRect,Offleft,OffTop,OffRight,OffBottom); { drawing area }
with bRect do begin
OffRowBytes := (((right - left -1) div 16) +1) * 2;{has to be even!}
SizeOfOff := (bottom - top) * OffRowBytes;
OffSetRect(bRect,-left,-top); {local coordinates...0,0 = topleft }
end; { of with }
with OffScreen do begin; { create new BitMap record }
{create new bitimage and make baseAddr point to it}
baseAddr := QDPtr(NewPtr(SizeOfOff));
rowbytes := OffRowBytes;{number of bytes/row,can extend beyond bounds}
bounds := bRect;{limits of bitimage drawing?}
end; { of with OffScreen }
End;
procedure DrawPicsIntoOffScreen;
var
i: integer;
Begin
OldBits := myWindow^.portBits; {preserve old myWindow BitMap}
SetPortBits(OffScreen); {our new myWindow BitMap }
{note: If offscreen bitmap is bigger than myWindow bitmap watchout for
clipping caused by ClipRgn and VisRgn fields of grafport record, you
can set cliprgn with ClipRect procedure and use CopyRgn procedure
to store old visrgn in temporary rgn... etc.}
FillRect(myWindow^.PortRect,white); {erase our new BitMap to white}
{locate wagon shape Rectangles in OffScreen Bitmap}
OffSetRect(OffWagonRect[1],-OffWagonRect[1].left,
-OffWagonRect[1].top);{topleft corner..0,0}
OffSetRect(OffWagonRect[2],-OffWagonRect[2].left,
OffWagonRect[1].bottom - OffWagonRect[2].top);{below 1}
OffSetRect(OffWagonRect[3],-OffWagonRect[3].left,
OffWagonRect[2].bottom - OffWagonRect[3].top);{below 2}
{draw the wagons into the offscreen bitmap}
For i := 1 to 3 do DrawPicture(Wagon[i],OffWagonRect[i]);
{locate the flys in the offscreen bitmap}
OffSetRect(OffFly[1],OffWagonRect[1].right - OffFly[1].left,
-OffFly[1].top); {right of wagons, at top}
OffSetRect(OffFly[2],OffWagonRect[1].right - OffFly[2].left,
OffFly[1].bottom-OffFly[2].top); {right of wagons, below fly[1]}
{draw the flys into offscreen}
For i := 1 to 2 do DrawPicture(Fly[i],OffFly[i]);
{could do a 'ReleaseResource' here if you're done with the Pictures}
SetPortBits(OldBits); {restore old bitmap}
end;
procedure DrawWindowContents(WhichWindow:WindowPtr);{response to Update event}
var
trect:Rect;
i:integer;
Begin
DrawPicture(HomeTown,HomeRect);{draw our logo}
{draw groundline under wagon}
MoveTo(myWindow^.portRect.left,WagonRect.bottom);
LineTo(myWindow^.portRect.right,WagonRect.bottom);
{copy offScreen Wagons & flys into Window..upperleft corner,as in bitmap}
CopyBits(OffScreen,myWindow^.portBits,OffScreen.bounds,
OffScreen.bounds,srcCopy,nil);
{draw current wagon Shape being animated into WagonRect}
CopyBits(OffScreen,myWindow^.portBits,OffWagonRect[WagonNdx],
WagonRect,srcCopy,nil);
{all the fly stuff}
DrawPicture(Flower,FlowerRect);
CopyBits(OffScreen,myWindow^.portBits,OffFly[FlyNdx],
FlyRect,srcCopy,FlowerRgn);
FrameRoundRect(FlyLimits,48,32);{border around the fly area}
End;
Procedure InitialAnimation;{locate everything to begin animation}
var
tRgn:RgnHandle;
Begin
{locate first WagonRect,left side of window on groundline (250)}
OffSetRect(WagonRect,-WagonRect.left,250-WagonRect.bottom);
WagonNdx := 1;{set to first Wagon shape}
{locate the flower}
OffSetRect(FlowerRect,160-FlowerRect.left,90-FlowerRect.top);
{locate the FlowerRgn in sync with Flower}
OffSetRgn(FlowerRgn,FlowerRect.left-FlowerRgn^^.rgnBBox.left,
FlowerRect.top-FlowerRgn^^.rgnBBox.top);
{size the FlyLimits}
FlyLimits := FlowerRect;
InsetRect(FlyLimits,-18,0);{expand left/right for border}
FlyLimits.top := FlyLimits.top - 18;{also top.. leave bottom for stem}
{create the region to exclude drawing in the flower}
tRgn := NewRgn;
OpenRgn;
FrameRoundRect(FlyLimits,48,32);
CloseRgn(tRgn);{region of limits rectangle}
DiffRgn(tRgn,FlowerRgn,FlowerRgn);{take out flower,put result in FlowerRgn}
{expand limits by 1 so we can have a frame border}
InSetRect(FlyLimits,-1,-1);
FlightRect := FlyLimits;{FlightRect will compensate for using fly.topleft}
InSetRect(FlightRect,-20,-20);{expand it so fly will be outside border}
OffSetRect(FlightRect,-20,-20);{compensate for using flyRect.topleft}
MouseRect := FlightRect;{MouseRect moves with cursor,& map into FlightRect}
{locate fly in upperleft of FlightRect}
OffSetRect(FlyRect,FlightRect.left-FlyRect.left,
FlightRect.top-FlyRect.top);
FlyNdx := 1;{set to first Fly shape}
end;
procedure AnimateWagon;
var tPoint:Point;
tRect:Rect;
Begin
{locate next WagonRect for wagon shape}
If WagonRect.left > myWindow^.portRect.right then
OffSetRect(WagonRect,-WagonRect.right,0) {back to left side}
else OffSetRect(WagonRect,1,0); {move one pixel to right}
{draw Current WagonNdx shape into WagonRect}
CopyBits(OffScreen,myWindow^.portBits,OffWagonRect[WagonNdx],
WagonRect,srcCopy,nil);
{next wagon shape to be drawn}
If WagonNdx < 3 then inc(WagonNdx) {next shape}
else WagonNdx := 1; {back to first shape}
{now animate the fly}
GetMouse(tPoint);{get current mouse coordinates}
{hide cursor if its over the fly area}
If PtInRect(tPoint,FlyLimits) then begin
If CursorIsOn then begin {hide cursor if its on}
HideCursor;
CursorIsOn := False;
end;
end
else If not(CursorIsOn) then begin {show cursor if its off}
ShowCursor;
CursorIsOn := True;
end;
{limit fly image (FlyRect) to FlightRect extremes..}
{to keep fly from wondering off visible area}
{mouseRect moves with the cursor and tPoint is mapped into FlightRect}
If not(PtInRect(tPoint,MouseRect)) then begin
{a coordinate is outside mouseRect so slide mouseRect to tPoint}
If tPoint.h < MouseRect.left then begin {slide MouseRect to left}
MouseRect.right := MouseRect.right -
(MouseRect.left - tPoint.h);
MouseRect.left := tPoint.h;
end
else If tPoint.h > MouseRect.right then begin
MouseRect.left := MouseRect.left + tPoint.h - MouseRect.right;
MouseRect.right := tPoint.h;
end;
If tPoint.v < MouseRect.top then begin
MouseRect.bottom := MouseRect.bottom -
(MouseRect.top - tPoint.v);
MouseRect.top := tPoint.v;
end
else If tPoint.v > MouseRect.bottom then begin
MouseRect.top := MouseRect.top + tPoint.v - MouseRect.bottom;
MouseRect.bottom := tPoint.v;
end;
end;{if not(ptinRect)}
{tPoint is to Mouse as FlyRect.topleft is to FlightRect}
MapPt(tPoint,MouseRect,FlightRect);
{determine horizontal offset if needed}
If tPoint.h > FlyRect.left + 2 then begin
FlyRect.left := FlyRect.left + 3;{offsetRect to right}
FlyRect.right := FlyRect.right + 3;
end
else if tPoint.h < FlyRect.left - 2 then begin
FlyRect.left := FlyRect.left - 3;{offsetRect to left}
FlyRect.right := FlyRect.right - 3;
end;
{vertical offset?}
If tPoint.v > FlyRect.top + 2 then begin
FlyRect.top := FlyRect.top + 3;
FlyRect.bottom := FlyRect.bottom + 3;
end
else if tPoint.v < FlyRect.top - 2 then begin
FlyRect.top := FlyRect.top - 3;
FlyRect.bottom := FlyRect.bottom - 3;
end;
CopyBits(OffScreen,myWindow^.portBits,OffFly[FlyNdx],
FlyRect,srcCopy,FlowerRgn);
If FlyNdx = 1 then inc(FlyNdx) {next shape, there are 2}
else FlyNdx := 1; {back to first shape}
end;
PROCEDURE InitThings;
Begin
InitGraf(@thePort); {create a grafport for the screen}
MoreMasters; {extra pointer blocks at the bottom of the heap}
MoreMasters; {this is 5 X 64 master pointers}
MoreMasters;
MoreMasters;
MoreMasters;
{get the cursors we use and lock them down - no clutter}
ClockCursor := GetCursor(watchCursor);
HLock(Handle(ClockCursor));
{show the watch while we wait for inits & setups to finish}
SetCursor(ClockCursor^^);
{init everything in case the app is the Startup App}
InitFonts; {startup the fonts manager}
InitWindows; {startup the window manager}
InitMenus; {startup the menu manager}
TEInit; {startup the text edit manager}
InitDialogs(Nil); {startup the dialog manager}
Finished := False; {set program terminator to false}
FlushEvents(everyEvent,0); {clear events from previous program}
{ set up screen size stuff }
Screen := ScreenBits.Bounds; { Get screen dimensions from thePort }
with Screen do { Screen.Left, etc. }
Begin
SetRect(DragArea,Left+4,Top+24,Right-4,Bottom-4);
SetRect(GrowArea,Left,Top+24,Right,Bottom);
End;
End;
procedure CreateWindow;
var
Wrect: Rect;
TypeWind: Integer;
Visible: Boolean;
GoAway: Boolean;
RefVal: Longint;
Begin
SetRect(Wrect,10,40,500,300);
TypeWind := 0;
Visible := True;
GoAway := True;
myWindow := NewWindow(Nil,Wrect,'Simple Animation',Visible,
TypeWind,Nil,GoAway,RefVal);
SetPort(myWindow);
ClipRect(myWindow^.portRect);{set clipping area as per Inside Mac}
End;
procedure DoMenuCommand(mResult:LongInt);
var
name: Str255;
tPort: GrafPtr;
Begin
theMenu := HiWord(mResult);
theItem := LoWord(mResult);
Case theMenu of
appleMenu:
Begin
GetItem(myMenus[1],theItem,name);
refNum := OpenDeskAcc(name);
End;
fileMenu:
Case theItem of
1:{display offScreen bitmap in dialog}
Begin
myDialog := GetNewDialog(128,nil,myWindow);{from resource}
GetPort(tPort);
ShowWindow(myDialog);{invisible in resource}
SelectWindow(myDialog);
SetPort(myDialog); {so we can draw into our window}
CopyBits(OffScreen,myDialog^.portBits,OffScreen.bounds,
OffScreen.bounds,srcOr,Nil);{the whole thing}
FrameRect(OffScreen.bounds);{frame it }
MoveTo(OffScreen.bounds.left + 10,OffScreen.bounds.bottom + 20);
DrawString('^ copy of OffScreen Bitmap');
ModalDialog(Nil,theItem);{we'll close no matter what hit}
HideWindow(myDialog);
SelectWindow(myWindow);{restore our game window}
SetPort(tPort);
end;{1:}
2:Finished := True;
end;{case theItem}
End;
HiliteMenu(0);
End;
procedure TakeCareMouseDown(myEvent:EventRecord);
var
Location: integer;
WhichWindow: WindowPtr;
MouseLoc: Point;
WindowLoc: integer;
Begin
MouseLoc := myEvent.Where; {Global coordinates}
WindowLoc := FindWindow(MouseLoc,WhichWindow); {I-287}
case WindowLoc of
inMenuBar:
DoMenuCommand(MenuSelect(MouseLoc));
inSysWindow:
SystemClick(myEvent,WhichWindow); {I-441}
inContent:
If WhichWindow <> FrontWindow then
SelectWindow (WhichWindow);
inGoAway:
If TrackGoAway(WhichWindow,MouseLoc) then
Finished := True;{end the game}
end; {case of}
end; { TakeCareMouseDown }
procedure TakeCareActivates(myEvent:EventRecord);
var
WhichWindow: WindowPtr;
Begin
WhichWindow := WindowPtr(myEvent.message);
SetPort(WhichWindow);
End;
procedure TakeCareUpdates(Event:EventRecord);
var
UpDateWindow,TempPort: WindowPtr;
Begin
UpDateWindow := WindowPtr(Event.message);
GetPort(TempPort);
SetPort(UpDateWindow);
BeginUpDate(UpDateWindow);
EraseRect(UpDateWindow^.portRect);{ or UpdateWindow^.VisRgn^^.rgnBBox }
DrawWindowContents(UpDateWindow);
EndUpDate(UpDateWindow);
SetPort(TempPort);
End;
procedure MainEventLoop;
var
myEvent: EventRecord;
Begin
InitCursor;
CursorIsOn := True;{keep track of hide/show cursor}
Repeat
SystemTask;
If GetNextEvent(EveryEvent,myEvent) then
Case myEvent.What of
mouseDown: TakeCareMouseDown(myEvent);
KeyDown: Finished:= True;
ActivateEvt:TakeCareActivates(myEvent);
UpDateEvt: TakeCareUpdates(myEvent);
End
Else AnimateWagon;{animate one loop of our wagon shape}
Until Finished;
End;
procedure SetUpMenus;
var
i: integer;
Begin
myMenus[1] := GetMenu(appleMenu); {get menu info from resources}
AddResMenu(myMenus[1],'DRVR'); {add in all the DA's}
myMenus[2] := GetMenu(fileMenu);
For i := 1 to lastMenu do
begin
InsertMenu(myMenus[i],0);
end;
DrawMenuBar;
End;
procedure CloseStuff;
Begin
{be sure to kill any sound I/O before quitting!}
End;
{Main Program begins here}
BEGIN
InitThings;
SetUpMenus;
CreateWindow;
CreatePictures;{load picts from resource file}
CreateOffScreenBitMap;{for use with copyBits procedure}
DrawPicsIntoOffScreen;{OffScreen holds all our wagon/fly shapes}
InitialAnimation;{set up stuff for start of animation}
MainEventLoop;{will animate wagon/fly if nothing else is going on}
CloseStuff;
END.