Improving the Performance of Animating Sprites in cocos2d

August 5th, 2009 by Derek van Vliet Leave a reply »

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

  • Jer
    You guys rock. This totally solved our performance issue
  • Cool glad to know we could be of some help. What are you guys working on? Anything you can publicly talk about?
  • nalinsharma
    Thanks Derek

    I was looking for a way for the animation to not restore the original frame after playing a 1 shot animation.

    Not only did you solve this problem, but you improved performance as well.

    Thank you so much!

    Best of luck with your app!
  • Great tip. Tkx!
blog comments powered by Disqus