Loading Cocos2D Sprite Frame Animations from Plist Files

John WordsworthiOS Development18 Comments

In a game that a friend and I are working on (Knight Terrors) we wanted a system to pre-load animations into CCAnimationFrameCache without having to hardcode any of that configuration. This means that our designer and artist, Jackson Matthews, can add and remove frames from a creature’s animation without having to come back to me with a frame list to paste back into the code. I will assume you have knowledge of CCSpriteFrameCache and CCAnimations within cocos2d before embarking on this. If not, you can read up on them through the provided links.

Important Note. This feature has been part of cocos2d since v1.1 and may have changed since. This is kept here for historical purposes only and to provide some understanding to why this feature was written.

Introduction to CCSpriteFrameCache.
How to Animate Sprites in Cocos2D.

Background: We’re currently developing a game that makes heavy use of Cocos2D for the iPhone / iPod / iPad. I’m building up to writing a tutorial on how we implemented a system to allow actions to trigger when a given animation frame is reached for a given entity. However, as that’s not quite there yet – I wanted to share something a little bit simpler, but something that I consider to be very useful…

CCAnimationCacheExtensions: We developed a pretty simple extension for CCAnimationCache that will allow you to build CCAnimations from the information contained in a plist file that contains a list of animation frames and a delay value for a selection of animations. In our model, we created a plist file for each creature, and this plist file is modelled in a specific way, as depicted below;

Image showing an example plist file for adding animations to CCAnimationCache automatically

Example plist file for adding animations to CCAnimationCache automatically (click to enlarge)

As shown in image of the plist file above, we have a standard plist file with a Dictionary root node. Within that, we have a dictionary named ‘animations’ which we will use to store all of the animations that we want to load with our code. In this dictionary, we then hold another dictionary for each animation we want to load, with the key being the name of the animation that we want to load into the cache, and the value of that animation being a another dictionary. Each dictionary at this level represents a CCAnimation, containing a frames array that lists the name of each frame you want to be part of the animation and a delay number which represents the time delay between switching frames.

Important note: the frames you list in your animations plist file must already exist in the CCSpriteFrameCache before you load in this plist file.

Loading this plist file into the CCAnimationCache is achieved by calling [[CCAnimationCache sharedAnimationCache] addAnimationsWithFile:@”path.plist”]. We add this functionality to CCAnimationCache without editing any part of the cocos2d library by using an Objective-C Category, which allows us to add additional methods to a class defined elsewhere without editing the original files (keeping our changes upgrade safe).

The code we use to load these animations is as follows;


#import "cocos2d.h"

@interface CCAnimationCache (ISExtensions)

-(void)addAnimationsWithDictionary:(NSDictionary *)dictionary;
-(void)addAnimationsWithFile:(NSString *)plist;



#import "CCAnimationCacheExtensions.h"
@implementation CCAnimationCache (ISExtensions)

/** Add animations to the cache from an NSDictionary that contains an 'animations' element at it's root. */
-(void)addAnimationsWithDictionary:(NSDictionary *)dictionary
	NSDictionary *animations = [dictionary objectForKey:@"animations"];

	if ( animations == nil ) {
		CCLOG(@"ISCCAnimationCacheExtensions: No animations found in provided dictionary.");

	NSArray* animationNames = [animations allKeys];

	for( NSString *name in animationNames ) {
		NSDictionary* animationDict = [animations objectForKey:name];
		NSArray *frameNames = [animationDict objectForKey:@"frames"];
		NSNumber *delay = [animationDict objectForKey:@"delay"];
		CCAnimation* animation = nil;

		if ( frameNames == nil ) {
			CCLOG(@"ISCCAnimationCacheExtensions: Animation '%@' found in dictionary without any frames - cannot add to animation cache.", name);

		NSMutableArray *frames = [NSMutableArray arrayWithCapacity:[frameNames count]];

		for( NSString *frameName in frameNames ) {
			CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:frameName];
			CCLOG(@"ISCCAnimationCacheExtensions: Animation '%@' refers to frame '%@' which is not currently in the CCSpriteFrameCache. This frame will not be added to the animation.", name, frameName);

			if ( frame != nil ) {
				[frames addObject:frame];

		if ( [frames count] == 0 ) {
			CCLOG(@"ISCCAnimationCacheExtensions: None of the frames for animation '%@' were found in the CCSpriteFrameCache. Animation is not being added to the AnimationCache.", name);
		} else if ( [frames count] != [frameNames count] ) {
			CCLOG(@"ISCCAnimationCacheExtensions: An animation in your dictionary refers to a frame which is not in the CCSpriteFrameCache. Some or all of the frames for the animation '%@' may be missing.", name);

		if ( delay != nil ) {
			animation = [CCAnimation animationWithFrames:frames delay:[delay floatValue]];
		} else {
			animation = [CCAnimation animationWithFrames:frames];

		[[CCAnimationCache sharedAnimationCache] addAnimation:animation name:name];

/** Read an NSDictionary from a plist file and parse it automatically for animations. */
-(void)addAnimationsWithFile:(NSString *)plist
	NSString *directory = [plist stringByDeletingLastPathComponent];
	NSString *file = [plist lastPathComponent];
	NSString *path = [[NSBundle mainBundle] pathForResource:file ofType:nil inDirectory:directory];

	NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];

	if ( dict == nil ) {
		CCLOG(@"ISCCAnimationCacheExtensions: Couldn't load animations from plist file.");
	} else {
		[self addAnimationsWithDictionary:dict];


The addAnimationsWithFile:(NSString *)plist method simply loads a plist file into an NSDictionary and then passes it to the other method, which does the grunt work. The method addAnimationsWithDictionary:(NSDictionary *)dictionary does the work of iterating through the relevant elements of the NSDictionary that was read from the plist file and creating a CCAnimation object for each inner dictionary that contains valid ‘frames’ (and optionally, a delay).

Playing an animation from the cache is then a case of creating a CCAnimate action and running it on the sprite you want to animate, like so;

CCAnimation *animation = [[CCAnimationCache sharedAnimationCache] animationByName:@"knight-walk-left01.png"];

if ( animation != nil ) {
	CCAction *action = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO]];
	[self.sprite runAction:action];

I hope you find this useful. If you have any questions about how it works, please don’t hesitate to get in touch below.

If you’re interested, here’s a shot of the code in action, in our upcoming iOS game ‘Knight Terrors’.

An early screenshot of Knight Terrors; please ignore the prototype icons at the bottom of the screen!

John WordsworthLoading Cocos2D Sprite Frame Animations from Plist Files

18 Comments on “Loading Cocos2D Sprite Frame Animations from Plist Files”

    1. John Wordsworth

      That’s fantastic – thanks for the comment.

      I’ve recently finished some code that fires CCActions when you ‘start’ specific CCAnimation frames (so you can have a character fire a bullet on ‘attack frame 7’ for instance). When I’ve tidied that up I’ll be sure to either share it back on google code or submit it as a possible extension. However, we have been busy with client work recently – which has put a pause button on the game development for a while (just like GameDevStory!).

  1. Kirian

    Hi, John!
    Thanx a lot for this extenstion!

    P.S. You probably wanted to log out a warning about null frame in line 31 in case then “frame == nil”.

    Good luck!

    1. John Wordsworth

      Hi Kirian, nice spot – many thanks. I’ve got a couple of other minor edits to make to this post very soon, so I’ll do these as a batch when I next sit down and go through my blog change list.

      Thanks for commenting and best of luck with your Cocos2D projects!

      1. SeruK

        I know this post is old, and that this extension is actually included in Cocos 1.1, but your CCLOG on line 31 doesn’t actually check if the frame is nil; it just complains no matter what*.

        * kind of like my mother-in-law! Hawhawhaw!
        Nah, just kidding she’s great.

    2. Precious

      25. januar 2010På tide. Forhåbentlig vil det give den flere læsere.Jeg læste hele serien i går. Glæder mig til den en dag er færdig, så man forhåbentlig kan læse det hele i 1 stræk, uden at skulle ind i x-antal tråde eller som jeg gjorde: sidde og ændre bi.&8deURLl#e217;enlFaktisk har jeg læst de første noget med 30 sider før, men det var fordi jeg troede serien var færdig på daværende tidspunkt. Men ak.PT er det en af de eneste webcomics jeg følger med i.

  2. Jady

    Hi John Wordsworth, would you help tell me the tool’s name which generate the animation file? (Basic-knight-def.plist)
    I think it is very useful, and how can we get?

    1. auto insurance

      I was just starting to treat TMJ disorders, headaches and chronic pain when I was first shown Dr Travell’s text. I ordered it the next day and it changed my life and help me drastically improve the lives of hundreds of patients. I only had a couple two times where I spent a day learnig from Dr Travell and they will always be two of the best days of my life.When I teach her book is one I recommend that every doctor requires in their practices. I will forever be indebted to Dr Travell.

    2. car insurance

      “Spreading concomitant with the dispersal of Austronesian languages” does not even suggest anything about “the purest descendants of the original Proto-Austronesian speakers.” How many times must I repeat this?

    3. http://www./

      the way I express myself, I like to write my comments fast.First: Admit you’re religious yourself.Second, you won’t convert me or anyone here;)Third: What do you mean by omnipotent.And last: They should first prove there is a god.

    4. http://www./

      This was a great documentary. it has helped to confirm my view that despite the good intentions of all founders of religion, including mohamed, the organised religion of today are devices purely used to control and divide people who otherwise have alot in common; knowledge

    5. http://www./

      Your argument is strained. DC has had black mayors who are very attuned to "white interests", like Anthony Williams and Adrian Fenty. Compared to my old home, Chicago, blacks and whites here in DC get along remarkably well. The large number of educated blacks in the DC area blunts black resentment.

    6. online auto insurance quotes

      Auzisem ca a aparut, dar nu stiam ca in distributie ii are pe doi dintre actorii mei preferati, Keira Knightley si Jude Law. Iar acum, ca ne-ai dezvaluit ca filmul are un alt final decat cel din carte, curiozitatea mea s-a dublat. Trebuie sa-l vad.

    7. http://www./

      I find it fas­ci­nat­ing that while we Amer­i­cans are ignor­ing our basic rights, there are those halfway around the globe that are express­ing their views legally for the first time in half a cen­tury. Say it loud and clear: I am a Sunni, I am an Iraqi and I voted. Today I can walk tall, I can say I am Iraqi with a proud not only because I voted but also I fought against ter­ror­ist with my vot­ing bal­lot. I stepped out the car, the vot­ing cen­ter was on the other side of the street, young Iraqi teenagers were

Leave a Reply

Your email address will not be published. Required fields are marked *