cocos2d has proven to be invaluable in enabling us to get our iPhone game up and running fast. But when we started playing with animated sprites, we noticed some performance problems. If you are like us and have many sprites that have to have repeating animations, you may have noticed the same thing.
Conventional wisdom (aka the sample code) says that you would create a RepeatForever action using an Animate action to animate an AtlasSprite. When we tried this, however, we noticed that at the end of each animation cycle, the action would deallocate an AtlasSpriteFrame. This is a by-product of the Animate action being able to restore the original frame of animation once the animation has finished playing. This is also what was causing the performance issues.
To resolve this, we created a cut-down version of the Animate action called FastAnimate. It is very similar to the Animate action with the exception that it does not support restoring the original frame of animation. From what we’ve seen, this simple change greatly increases the performance of animating sprites in cocos2d.
FastAnimate.h:
#import "cocos2d.h" @interface FastAnimate : IntervalAction <NSCopying> { Animation *animation; } +(id) actionWithAnimation:(id<CocosAnimation>) a; -(id) initWithAnimation:(id<CocosAnimation>) a; @end
FastAnimate.m:
#import "FastAnimate.h" @implementation FastAnimate +(id) actionWithAnimation: (id<CocosAnimation>)anim { return [[[self alloc] initWithAnimation:anim] autorelease]; } -(id) initWithAnimation: (id<CocosAnimation>)anim { NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil"); if( (self=[super initWithDuration: [[anim frames] count] * [anim delay]]) ) { animation = [anim retain]; } return self; } -(id) copyWithZone: (NSZone*) zone { return [[[self class] allocWithZone: zone] initWithAnimation: animation]; } -(void) dealloc { [animation release]; [super dealloc]; } -(void) startWithTarget:(id)aTarget { [super startWithTarget:aTarget]; } -(void) stop { [super stop]; } -(void) update: (ccTime) t { NSUInteger idx=0; ccTime slice = 1.0f / [[animation frames] count]; if(t !=0 ) idx = t/ slice; if( idx >= [[animation frames] count] ) { idx = [[animation frames] count] -1; } id<CocosNodeFrames> sprite = (id<CocosNodeFrames>) target; if (! [sprite isFrameDisplayed: [[animation frames] objectAtIndex: idx]] ) { [sprite setDisplayFrame: [[animation frames] objectAtIndex:idx]]; } } @end
