bunny meets cocos2d-x, part 1: sprite sheets and animations (an illustrated guide)

i’m learning cocos2d-x1. i’m taking notes. the book cocos2d-x by example: a beginner’s guide is a big help.

cocos2d-x’s systems for handling spritesheets and animations is complex. the aforementioned book has practical tutorials, but i wanted a high-level conceptual guide to all of the pieces. here is my attempt at one, based on my exploration of the cocos2d-x source code.

note

i am still learning this, so i apologize for any inaccuracies. please send me corrections and clarifications in the comments!

the classes

i’ve conceptually separated the relevant classes into three groups: frames, rendering, and animation (these are not official groupings: they’re my own).

  • frames
  • cocos2d::SpriteFrame
  • cocos2d::SpriteFrameCache
  • rendering
  • cocos2d::Sprite
  • cocos2d::SpriteBatchNode
  • animation
  • cocos2d::Animation
  • cocos2d::Animate

versions and namespaces

  • i’m using cocos2d-x 3.0 alpha 0. names and functionality may differ in other versions.
  • for the rest of this post i’ll be leaving out the cocos2d:: namespace. assume using namespace cocos2d;

let’s start with frames.

frames

SpriteFrameCache is a singleton that knows how to parse spritesheet files generated by tools like zwoptex and texturepacker (among others). when SpriteFrameCache reads a spritesheet file, it creates a SpriteFrame object for every frame in the file.

SpriteFrameCache reading a spritesheet file and creating SpriteFrame objects

each SpriteFrame object contains:

when loading a spritesheet file, the SpriteFrameCache also loads the associated texture into memory.3

once you’ve loaded your spritesheet file, your game can query the SpriteFrameCache for any SpriteFrame, by name.

SpriteFrameCache* spriteFrameCache = SpriteFrameCache::getInstance();

//  load the spritesheet file.
spriteFrameCache->addSpriteFramesWithFile("spritesheet.plist");

//  get a SpriteFrame using a name from the spritesheet.plist file.
SpriteFrame* spriteFrame = spriteFrameCache->getSpriteFrameByName("ship.png");

rendering

a SpriteFrame is essentially a data object: it does not display or render anything on its own.

meanwhile, a Sprite is an onscreen object that renders itself using a texture and texture rectangle. sound familiar? this is the same information that a SpriteFrame provides.

you can assign a SpriteFrame to a Sprite with the method Sprite::setDisplayFrame(SpriteFrame *). this will update the Sprite’s texture and texture rectangle to that of the SpriteFrame. thus, when the Sprite draws itself, it will essentially render the SpriteFrame to the screen.4

sprite->setDisplayFrame(spriteFrame) results in changed sprite

//  continuing from the example above:

Sprite* sprite = Sprite::create();
sprite->setDisplayFrame(spriteFrame);
//  this sprite will now draw itself using the "ship.png" frame.

//  or you can get a SpriteFrame and create a Sprite with it in one step, like this:
Sprite* anotherSprite = Sprite::createWithSpriteFrameName("ship.png");

batch drawing with spritebatchnode

if a bunch of sprites are rendering from the same texture (probably with different texture rects), then they can be rendered much more efficiently in a batch drawing operation. to take advantage of this, you have to manually add them5 as children of a SpriteBatchNode, which needs to be initialized with the same texture that all of its child sprites use.

SpriteBatchNode and its child Sprites

if you do not use a SpriteBatchNode, each Sprite will be drawn with a separate OpenGL drawing operation, which is much slower.

//  SpriteBatchNode must be created with the same texture that its child sprites use.
SpriteBatchNode* spriteBatchNode = SpriteBatchNode::create(sprite->getTexture());
spriteBatchNode->addChild(sprite);

animations

you can animate a Sprite’s appearance by assigning different SpriteFrames to a Sprite, over time.

if you want to string together a sequence of frames to play back in order, automatically, you’ll need the Animation and Animate classes.

note

despite the name similarities, Animation and Animate are two distinct classes that work together, but do very different things.

an instance of the Animation class contains an array of pointers to SpriteFrames, in the order the are to be played, along with some timing information. it’s basically just a data object.

diagram of Animation class

just as a SpriteFrame doesn’t do any drawing, an Animation doesn’t do any animating. to actually change a Sprite’s appearance over time, you’ll need an instance of the Animate class.

Animate is a subclass of Action. actions are objects that can manipulate the state of a Sprite over time. in this case, the Animate action reads an Animation object’s SpriteFrames, and applies each one to a Sprite, one after the other, with a delay in between.

think of an Animation object like a roll of film, which the projector (the Animate object) “projects” onto the Sprite.

diagram of Animation and Animate classes

here’s a simple snippet that manually builds an Animation and attaches it to a Sprite:

//  in this example, a spritesheet has already been loaded, with SpriteFrames
//  named "frame0.png", "frame1.png", "frame2.png", etc.

//  create the Animation, and populate it with frames fetched from the SpriteFrameCache

Animation *anim = Animation::create();
for (int i = 0; i < 6; i ++)
{
    std::ostringstream filenameStream;
    filenameStream << "frame" << i << ".png";
    SpriteFrame *frame = spriteFrameCache->getSpriteFrameByName(filenameStream.str().c_str());
    anim->addSpriteFrame(frame);
}
//  we can also set timing information
anim->setDelayPerUnit(0.2f);

//  we now have an Animation object that describes what frames to display,
//  and in what order.

//  next, create the Action that actually alters the Sprite.

Animate *animateAction = Animate::create(anim);

//  finally, we can execute the action.

sprite->runAction(animateAction);

what’s next

manually specifying the frames in code for every animation can get tedious. this is where cocos2d::AnimationCache comes in… but i will save that for a future instalment.


  1. cocos2d-x is the cross-platform, c++ offspring of cocos2d-iphone (which is itself the iOS offspring of cocos2d for python). 

  2. this is a bit simplified: there are also some attributes relating to texture rotation and trimming, too, which i am ignoring in this post for clarity’s sake. 

  3. namely, into the TextureCache singleton 

  4. i say “essentially” because the Sprite does not actually maintain any kind of reference to the SpriteFrame! it simply copies the SpriteFrame’s texture and texture rect information. 

  5. the cocos3 roadmap document talks about adding “automatic batching” in a future release. 

comments

comments powered by Disqus