This is GKActor.m in view mode; [Download] [Up]
// Handles moving and rendering various moving objects. #import <gamekit/gamekit.h> #import <stdio.h> @implementation GKActor - init // initialize the new instance vars { // You need to override this to set state and maxFrames at the very least [super init]; NX_X(&boundingBox) = 0; NX_Y(&boundingBox) = 0; NX_HEIGHT(&boundingBox) = GK_DEFAULT_ACTOR_WIDTH; NX_WIDTH(&boundingBox) = GK_DEFAULT_ACTOR_HEIGHT; GK_CLEAR_VECTOR(&nextLocation); GK_CLEAR_VECTOR(&lastLocation); GK_CLEAR_VECTOR(&lastDrawnLocation); maxFrames = (int *)malloc(sizeof(int)); maxFrames[0] = 0; maxSeries = 1; series = 0; frame = 0; cycles = 0; state = GK_DEAD_ACTOR; renderedImage = nil; drawingLevel = GK_DYNAMIC_ACTOR; return self; } - (int)drawingLevel { return drawingLevel; } - setDrawingLevel:(int)aLevel { drawingLevel = aLevel; return self; } - stage { return stage; } - setStage:aStage { // ***** probably ought to make sure we're not on any other stage! stage = aStage; return self; } - leaveTheStage { [stage removeActor:self ofType:drawingLevel]; return self; } - setSize:(const NXSize *)aSize // set the actor's size (enclosing frame in image) { GK_COPY_SIZES(&(boundingBox.size), aSize); return self; } - setRenderedImage:anImage // set the bitmap image used to render { // we don't free an old image, if it exists. This is because others // might also be using the image. What I should do is a reference // counting image ***** renderedImage = anImage; return self; } - setSeries:(int)anInt // set the animation series (row) to use in bitmap { if (anInt >= maxSeries) return nil; // only change to a valid series number series = anInt; return self; } - setMaxFrames:(const int *)anIntArray count:(int)anInt // array indexed by // series number; gives the number of frames in the series { // This could possibly be incorporated into an "Array" object, perhaps // one which does reference counting. ***** int i; free(maxFrames); maxFrames = (int *)malloc(sizeof(int) * anInt); for (i=0; i<anInt; i++) maxFrames[i] = anIntArray[i]; maxSeries = anInt; return self; } - move:sender // Move the Actor one animation frame { // you must override this and put something here // alter the nextLocation vector to move the actor. return self; } - (int)collisionType // called to determine which type of collision // detection to use with this actor { // override to change type... default is the bounding box return GK_RECTANGLE_SHAPE; } - (void *)shapeStruct // if you change the collision type, you must { // return a pointer to an appropriate structure defining the shape return (&boundingBox); // Note that if you call this method, do NOT // alter the boundingBox returned! It will cause all sorts of havoc! } - collidedWith:anActor // called when it is detected that we hit something { // you should override to do something about the hit, if necessary return self; } - lastAt:(NXPoint *)aPoint // called to find out where actor was { GK_COPY_VECTORS(aPoint, &lastLocation); return self; } - lastDrawnAt:(NXPoint *)aPoint // called to find out where actor was { GK_COPY_VECTORS(aPoint, &lastDrawnLocation); return self; } - at:(NXPoint *)aPoint // called to find out where actor was { GK_COPY_VECTORS(aPoint, &GK_location); return self; } - getBoundingBox:(NXRect *)box { GK_COPY_RECT(box, &boundingBox); return self; } - (int)series { return series; } // the series we're currently using - (int)frame { return frame; } // the next frame that will be drawn - moveOneFrame // moves the actor along; accessed via renderAt::move: { // flag movement; we won't redraw if no movement... if ((GK_VECTOR_X(&GK_location) != GK_VECTOR_X(&nextLocation)) || (GK_VECTOR_Y(&GK_location) != GK_VECTOR_Y(&nextLocation))) movedThisFrame = YES; GK_COPY_VECTORS(&GK_location, &nextLocation); GK_COPY_VECTORS(&lastLocation, &GK_location); return self; } - eraseInDirtPile:dirtPile { // pass our most recent dirty rect to the DirtPile... [dirtPile addRegion:GK_VECTOR_X(&lastDrawnLocation) :GK_VECTOR_Y(&lastDrawnLocation) :boundingBox.size.width :boundingBox.size.height]; return self; } - markInDirtPile:dirtPile // same as above but called at different times { // pass our most recent dirty rect to the DirtPile... [dirtPile addRegion:GK_VECTOR_X(&lastDrawnLocation) :GK_VECTOR_Y(&lastDrawnLocation) :boundingBox.size.width :boundingBox.size.height]; return self; } - renderAt:(NXPoint *)offset move:(BOOL)moveOk withDirtPile:dirtPile // draw actor; lock focus on view that gets the actor before call { // this should be overridden by the actor subclass NXPoint zero = { 0.0, 0.0 }; if (state == GK_DEAD_ACTOR) return self; // we're dead, nothing to do if (moveOk) [self moveOneFrame]; GK_COPY_VECTORS(&lastDrawnLocation, &GK_location); // we need to track // this so we can do erase properly if (movedThisFrame) { // handle the drawing if need to redraw. if (offset) [self drawActorWithOffset:offset]; else [self drawActorWithOffset:&zero]; // mark the drawn area as dirty [self markInDirtPile:dirtPile]; movedThisFrame = NO; } [self updateDrawingState]; // we update _even_ if no movement; // if you object is stationary, but changes shape a lot, then // you should set the movedThisFrame flag in this method! return self; } - drawActorWithOffset:(NXPoint *)offset // draw the actor { // 90% of the time you'll override this completely NXRect from = { { frame * NX_WIDTH(&boundingBox), series * NX_HEIGHT(&boundingBox) }, { NX_WIDTH(&boundingBox), NX_HEIGHT(&boundingBox) } }; NXPoint pos = { NX_X(&boundingBox) + GK_VECTOR_X(offset), NX_Y(&boundingBox) + GK_VECTOR_Y(offset) }; [renderedImage composite:NX_SOVER fromRect:&from toPoint:&pos]; return self; } - updateDrawingState // change the internal state machine (ie. advance { // frames, etc. This will also cycles++; frame++; if (frame >= maxFrames[series]) frame = 0; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.