Reza Ghobadinic

Creating Sprites With Animated Image Sequence

We can make sprites that play a sequence of animated or rendered images as an animation. In fact most sprites will be of this type in a game. A running or walking character, a tank, etc… are all examples of animated sprites.

 

Simple Example

Lets make a simple sprite in the middle of the screen that plays a simple animation.

I’ve made a simple basic sprite sheet for this test. Each image is a number from 0 to 9. Easy!

It looks something like this:

To create an animated sprite:

Vector<SpriteFrame*> frames;
frames.pushBack(spriteCache->getSpriteFrameByName("number0.png"));
frames.pushBack(spriteCache->getSpriteFrameByName("number1.png"));
frames.pushBack(spriteCache->getSpriteFrameByName("number2.png"));
frames.pushBack(spriteCache->getSpriteFrameByName("number3.png"));
frames.pushBack(spriteCache->getSpriteFrameByName("number4.png"));
frames.pushBack(spriteCache->getSpriteFrameByName("number5.png"));
frames.pushBack(spriteCache->getSpriteFrameByName("number6.png"));
frames.pushBack(spriteCache->getSpriteFrameByName("number7.png"));
frames.pushBack(spriteCache->getSpriteFrameByName("number8.png"));
frames.pushBack(spriteCache->getSpriteFrameByName("number9.png"));
auto animation = Animation::createWithSpriteFrames(frames);
animation->setDelayPerUnit(0.01);
animation->setLoops(-1);
auto action = Animate::create(animation);
sprite->runAction(action);

Now lets apply all this in Xcode and make it run:

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    float x = origin.x + visibleSize.width / 2;
    float y = origin.y + visibleSize.height / 2;

    auto spriteCache = SpriteFrameCache::getInstance();
    spriteCache->addSpriteFramesWithFile("numbers.plist");
    
    Vector<SpriteFrame*> frames;
    frames.pushBack(spriteCache->getSpriteFrameByName("number0.png"));
    frames.pushBack(spriteCache->getSpriteFrameByName("number1.png"));
    frames.pushBack(spriteCache->getSpriteFrameByName("number2.png"));
    frames.pushBack(spriteCache->getSpriteFrameByName("number3.png"));
    frames.pushBack(spriteCache->getSpriteFrameByName("number4.png"));
    frames.pushBack(spriteCache->getSpriteFrameByName("number5.png"));
    frames.pushBack(spriteCache->getSpriteFrameByName("number6.png"));
    frames.pushBack(spriteCache->getSpriteFrameByName("number7.png"));
    frames.pushBack(spriteCache->getSpriteFrameByName("number8.png"));
    frames.pushBack(spriteCache->getSpriteFrameByName("number9.png"));
    auto animation = Animation::createWithSpriteFrames(frames);
    animation->setDelayPerUnit(0.2);
    animation->setLoops(-1);

    auto action = Animate::create(animation);
    
    auto sprite = Sprite::createWithSpriteFrame(frames.front());
    // or just:
    // auto sprite = Sprite::create();
    sprite->setPosition(x, y);
    sprite->runAction(action);
    
    this->addChild(sprite, 0, 1);
    //this->scheduleUpdate();
    
    return true;
}

You can compile and run this and see your sprite showing the numbers one after another.

Download Source

 

Helper Function To Get The Animation Frames

It was OK to write the name of each frame separately for a test, but it is not fun to do it every time for every animation in a game!

We can make a helper function to facilitate this for us. Add the deceleration in the HelloWorldScene.h header file:

cocos2d::Vector<cocos2d::SpriteFrame *> getAnimationFrames(const char *format, int count);

The definition is as below. Note that this method expects the first image file start from 0 instead of 1 similar to arrays in C.

Vector<SpriteFrame *> HelloWorld::getAnimationFrames(const char *format, int count)
{
    auto spriteCache = SpriteFrameCache::getInstance();
    Vector<SpriteFrame *> frames;
    char str[100];
    for(int i = 1; i <= count; i++)
    {
        sprintf(str, format, i);
        auto frame = spriteCache->getSpriteFrameByName(str);
        frames.pushBack(frame);
    }
    return frames;
}

Now the updated init() method will be shorter and cleaner.

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    float x = origin.x + visibleSize.width / 2;
    float y = origin.y + visibleSize.height / 2;
    
    auto spriteCache = SpriteFrameCache::getInstance();
    spriteCache->addSpriteFramesWithFile("numbers.plist");
    
    auto frames = getAnimationFrames("number%d.png", 10);
    
    auto animation = Animation::createWithSpriteFrames(frames);
    animation->setDelayPerUnit(0.2);
    animation->setLoops(-1);
    
    auto action = Animate::create(animation);
    
    auto sprite = Sprite::createWithSpriteFrame(frames.front());
    sprite->setPosition(x, y);
    sprite->runAction(action);
    
    this->addChild(sprite, 0, 1);
    
    return true;
}

Download Source

We can have several ready animations that are made in this method and have our character sprites play different ones depending on which direction they go to or which behavior they need to show.

To do this, you should first stop the previous running action by calling stopAction() and then call runAction() again for a new animation.

Exit mobile version