After having a lot of trouble dealing with the issue myself and seeing others having trouble on the cocos2d discussions, I’m going to go ahead and post my solution which so far is working pretty well for me. Please take into account I’m new with Objective C when looking this over.
The problem is each AtlastSpriteManager can 1) refer only to a single sheet image, and 2) each AtlasSprite can only be a child of at AtlasSpriteManager. So what happens if you a few game characters that you want to share certain animations, but not all?
I took the super-class of all my game Sprites, changed its type from a Sprite (or AtlasSpriteManager, which I tried but failed to well) to a generic CocosNode. I added in this to the header. Note that I only needed my Sprites to switch between two different sprite sheets as most.
@interface Touchable : CocosNode <Collideable> { AtlasSprite *activeSprite; AtlasSprite *sprite1; AtlasSprite *sprite2; ... } - (id)init:(CGSize)sizeOne two:(CGSize)sizeTwo; - (AtlasSpriteManager *)spriteManager1; - (AtlasSpriteManager *)spriteManager2; - (void)setSprite:(NSUInteger)newstate; - (void)setActiveSpriteOne; - (void)setActiveSpriteTwo; ...
To the implementation file…
- (AtlasSpriteManager *)spriteManager1 { return nil; } - (AtlasSpriteManager *)spriteManager2 { return nil; } - (id)init:(CGSize)sizeOne two:(CGSize)sizeTwo { self = [super init]; if(self) { sprite1 = [[AtlasSprite spriteWithRect:CGRectMake(sizeOne.width, sizeOne.height, sizeOne.width, sizeOne.height) spriteManager: [self spriteManager1]] retain]; [[self spriteManager1] addChild:sprite1]; if(![[self spriteManager1] parent]) [[[LevelController sharedSingleton] gameLayer] addChild:[self spriteManager1] z:1]; [sprite1 setVisible:NO]; sprite2 = [[AtlasSprite spriteWithRect:CGRectMake(sizeTwo.width, sizeTwo.height, sizeTwo.width, sizeTwo.height) spriteManager: [self spriteManager2]] retain]; [[self spriteManager2] addChild:sprite2]; if(![[self spriteManager2] parent]) [[[LevelControler sharedSingleton] gameLayer] addChild:[self spriteManager2] z:1]; [sprite2 setVisible:NO]; } return self; } - (void)setSprite:(NSUInteger)newstate { } - (void)setActiveSpriteOne { activeSprite = sprite1; [sprite2 setVisible:NO]; [sprite1 setVisible:YES]; } - (void)setActiveSpriteTwo{ activeSprite = sprite2; [sprite2 setVisible:YES]; [sprite1 setVisible:NO]; }
A few methods of my superclass ‘Sprite’ object that I modify slightly to take changes into account. You can no longer want to run the actions (which include animations) on yourself, but on the active sprite.
- (void)setState:(enum ActionFigureState)newstate{ [activeSprite stopAction:[stateActions objectAtIndex:state]]; [self setSprite:newstate]; [activeSprite runAction:[stateActions objectAtIndex:newstate]]; state = newstate; } - (void) setPosition:(CGPoint)pos { [super setPosition:pos]; [activeSprite setPosition:pos]; }
So what we have now is a superclass that is calling a instance method [self spriteManager1] in order to manage attach its two different sprites. Notice that no animation specific code is here in the superclass, except for the fact that a single ‘Sprite’ can work with 2 AtlasSheetManagers at most. You could easily take away this restriction, but I had no need to complicate things.
This gets helpful when I get into a child class that actually knows how to draw itself. Here is my “SmallStick.m” which has an empty header file except for being a child of Touchable. This implementation code also contains all the this object’s interaction with the game, such as collision detection, how to handle if it is touched, and how to add itself to the Chipmunk space.
#define width1 79 #define height1 68 #define columns1 7 #define width2 79 #define height2 68 #define columns2 7 @implementation SmallStick static AtlasSpriteManager *spriteManager1 = nil; static AtlasSpriteManager *spriteManager2 = nil; + (AtlasSpriteManager *)spriteManager1 { @synchronized(spriteManager1) { // I dont actually know if synchronized is needed here if (!spriteManager1) { [Texture2D saveTexParameters]; [Texture2D setAliasTexParameters]; spriteManager1 = [[AtlasSpriteManager alloc] initWithFile:@"smallstick_common.png" capacity:50]; [Texture2D restoreTexParameters]; } return spriteManager1; } return nil; } + (AtlasSpriteManager *)spriteManager2 { @synchronized(spriteManager2) { if (!spriteManager2) { [Texture2D saveTexParameters]; [Texture2D setAliasTexParameters]; spriteManager2 = [[AtlasSpriteManager alloc] initWithFile:@"smallstick_weaponless.png" capacity:50]; [Texture2D restoreTexParameters]; } return spriteManager2; } return nil; } - (AtlasSpriteManager *)spriteManager1 { return [SmallStick spriteManager1]; } - (AtlasSpriteManager *)spriteManager2 { return [SmallStick spriteManager2]; } -(id)init { self = [super init:CGSizeMake(width1, height1) two:CGSizeMake(width2, height2)]; if(self) { ... } return self; } - (void) setSprite:(NSInteger)newstate { switch (newstate) { case fState_charging: case fState_attacking: case fState_recovering: [self setActiveSpriteTwo]; break; default: [self setActiveSpriteOne]; break; } } - (void)loadActions { AtlasAnimation *chargeA = [AtlasAnimation animationWithName:@"weaponless_charge" delay:0.05f]; for(int i=0;i<18;i++) { int x= i % columns2; int y= i / columns2; [chargeA addFrameWithRect: CGRectMake(x*width2, y*height2, width2, height2) ]; } Action *charge = [RepeatForever actionWithAction:[Animate actionWithAnimation: chargeA]]; ... stateActions = [[NSArray alloc] initWithObjects: charge, dying, ..., nil];
The part I like about this is when I want to reuse the ’smallstick_common.png’ atlas sheet I can just not override the class method +(AtlasSpriteManager *)spriteManager2 in my child classes.
Don’t forget to remove the sprite from the AtlasManager when you’re done.
[[self spriteManager1] removeChild:sprite1 cleanup:YES]; [[self spriteManager2] removeChild:sprite2 cleanup:YES];
First of all, I hope this doesn’t have some hidden downside. As I said, I’m new to the platform. But I hope somebody finds this useful.