1
\$\begingroup\$

I'm having trouble coming up with a way to animate these 2D isometric sprites. The sprites are stored like this:

(Game Folder Root)/Assets/Sprites/(Sprite Name)/(Sprite Animation)/(Sprite Direction)/(Frame Number).png

So for example,

/Assets/Sprites/Worker/Stand/North-East/01.png

Sprite sheets aren't really viable for this type of animation. The example stand animation is 61 frames. 61 frames for all 8 directions alone is huge, but there's more then just a standing animation for each sprite. Creating an sf::Texture for every image and every frame seems like it will take up a lot of memory and be hard to keep track of that many images. Unloading the image and loading the next one every single frame seems like it will do a lot of unnecessary work. What's the best way to handle this?

The reason I am not using a sprite sheet is because these sprites are hi-res images with many animations which all have a large quantity of frames. The resulting sprite sheet would be massive. In case you're wondering, here was my implementation of creating animated sprites.

// Animation class declaration
class Animation
{
public:
        Animation(std::string path, int frames);
        ~Animation();
        void nextFrame();
        void gotoStart();
        bool loadFrames();
        sf::Texture& getActiveFrame();

private:
        int frameCount;
        std::string pathToAnimation;
        int currentFrame;
        sf::Texture frame[];
};

// Animation class implementation
Animation::Animation(std::string path, int frames)
{
        pathToAnimation = path;
        frameCount = frames;
}

Animation::~Animation()
{
        // destructor
}

void Animation::nextFrame()
{
        if(currentFrame < frameCount)
        {
            currentFrame = 1;
        }
        else
            currentFrame += 1;
}

void Animation::gotoStart()
{
        currentFrame = 1;
}

bool Animation::loadFrames()
{
        for(int i = 01; i < frameCount; i++)
        {
            if(!frame[i].loadFromFile(pathToAnimation + std::to_string(i) + ".jpg")) return false;
        }
        return true;
}

sf::Texture& Animation::getActiveFrame()
{
    return frame[currentFrame];
}

// Actor class declaration
class Actor
{
public:
        Actor();
        ~Actor();
        void setActiveAnimation(std::shared_ptr<Animation> anim);
        void draw(sf::RenderWindow& win);

private:
        sf::Sprite sprite;
        std::shared_ptr<Animation> activeAnimation;
};

// Actor class implementation
Actor::Actor()
{
    // constructor
}

Actor::~Actor()
{
    // destructor
}

void Actor::setActiveAnimation(std::shared_ptr<Animation> anim)
{
    activeAnimation = anim;
    activeAnimation->gotoStart();
}

void Actor::draw(sf::RenderWindow& win)
{
    sprite.setTexture(activeAnimation->getActiveFrame());
    win.draw(sprite);
    activeAnimation->nextFrame();
}

It's still not 100% where I'd like it to be but it's working and it's pretty easy to use.

\$\endgroup\$
1
  • 4
    \$\begingroup\$ 1. You don't need 61 frames for a animation. Most should look really smooth with 16 frames already. (Depending on how long the animation is of course.) 2. It's huge, yes, but the computer has a huge amount of memory too. A 100MB (= a enormous amount of data) is a little nowadays. 3. Load them once and keep them. Its not "per Frame", its once. \$\endgroup\$
    – API-Beast
    Commented Jun 7, 2012 at 20:24

2 Answers 2

2
\$\begingroup\$

You would not want to load/unload a texture after each frame. This is because it would involve accessing the hard drive each time. This takes a very long time compared to accessing memory and you would certainly see a slow down in rendering. So what you want to do is to load all of the textures into memory.

If we have 60, 32x32, rgba8 textures for each 8 directions, this is about 2MB. So we can see that it really isn't that much memory.

(You could just keep each texture in memory and swap around, but the following is the more "correct" way to do it)

I believe you are using SFML, I haven't used it personally but after searching there is a class you can use just for this purpose, an sf::Sprite. This is just the same as rendering an sf::Texture, however you need to pass it a texture to start with. What's the difference? Well you need to pass this sprite a rectangle in the texture to use. So you say this sprite will use this part of the texture.

This is better because we can make one big texture with all of our sprites on and reference each sprite individually. It means we do not have to swap textures after each frame (this is expensive for the GPU to do).

Of course this means that you will have to add all of your separate sprites into one texture before you load them. It also means that you need to know the rectangle that each sprite occupies. After a quick google search you could use this SpriteSheetPacker; it outputs a final texture and a .txt file that gives you where each sprite is located.

\$\endgroup\$
1
  • \$\begingroup\$ In sfml, an sf::Texture is simply an image that we load into memory. The object we use to actually display the image and do things with it is sf::Sprite. I could use sf::Sprite::setTextureRect as you said if I were using a sprite sheet, but I stated in the edit why I chose not to do so. Thanks anyways. \$\endgroup\$
    – user7383
    Commented Jun 9, 2012 at 16:15
1
\$\begingroup\$

Rather than loading and unloading the textures on a per-frame basis, one possibility would be to load all the frames for a certain animation at once. For example, if you're about to play the attack animation, load all the frames for the attack animation(s) into memory if they aren't already there (checking whether the animation already exists in memory would have to be managed by a texture manager or some similar construct).

Another (probably better) option would be to combine all frames for a specific animation into a single animation-specific spritesheets, which is what Jeff Vogel from Spiderweb Software does for his 2D isometric RPG games. For example, you might combine all the defense animation frames (for all directions) into a single spritesheet, all the running frames (for all directions) into a single spritesheet, etc. Jeff's code is proprietary so I'm not sure how he handles the loading and unloading of the sprite sheets, but his games have good performance, and this approach strikes a good middle ground between having a single colossal spritesheet and having tons of single frames.

\$\endgroup\$

You must log in to answer this question.