Skip to content

Spritesheet and Animation

Madour edited this page Dec 21, 2020 · 5 revisions

Usually, a spritesheet refers to an image that contains multiple animations, where each animation is composed by multiple frames.

Let's learn how to translate an spritesheet image into a Spritesheet object you can use to play animations.

In NasNas, a Spritesheet object contains multiple Anim objects, where each Anim is composed by multiple AnimFrame objects. Close enough.

I will use this spritesheet for the example (from https://rvros.itch.io/animated-pixel-hero):

Creating a Spritesheet object

There are 2 ways to define animations in a Spritesheet object.

  • The first one is by using a grid, when you have a well made spritesheet where each sprite is in its own cell. The spritesheet we will use have each sprite in a cell of 50x37 pixels.
// create a Spritesheet from a name (can be anything) and a texture.
auto* spritesheet = new ns::Spritesheet("adventurer", ns::Res::in("sprites").getTexture("adventurer.png"));
// setting the grid from cell size and number of columns
spritesheet->setGrid({50, 37}, 7);
// after calling setGrid, the cells inside the sheet grid will be labeled starting from 0
// and increasing from left to right and top to bottom

// We can now add an animation using the labels of each frame.
// here, the animation "idle" starts at cell 0 and is 4 frames long, 
// each frame has a duration of 200 ms, finaly the origin of the frames will be at (25,35)
spritesheet->addAnim("idle", 0, 4, 200, sf::Vector2f(25, 35));

// when some frames in the anim have different durations or origins,
// you can pass a vector as argument
std::vector<int> walk_durations = {150, 150, 200, 150, 150, 200};
spritesheet->addAnim("walk", 8, 6, walk_durations, sf::Vector2f(25, 35));
  • The second method is by manually specifying the rectangles, duration and origin of each frame.
// adding the crouch animation manually and setting 
// the loop parameter to false (the animation stops when it ends)
auto* crouch_anim = new ns::Anim("crouch", {
    ns::AnimFrame({216, 15, 19, 21}, 200, {11, 21}),
    ns::AnimFrame({265, 14, 20, 22}, 200, {12, 22}),
    ns::AnimFrame({316, 14, 19, 22}, 200, {12, 22}),
    ns::AnimFrame({17, 52, 17, 21}, 200, {10, 21}),
}, false);
spritesheet->addAnim(crouch_anim);

Playing an animation

To play an animation, we need the Spritesheet (we created above), a Sprite and an AnimPlayer.

We already have a spritesheet, so we have to add a sf::Sprite and a ns::AnimPlayer member variables to the Game class header.

Game::Game() {
    /* insert spritesheet creation here */

    // playing the "idle" animation in the AnimPlayer
    this->anim_player.play(spritesheet->getAnim("idle"));

    // creating the sprite
    this->sprite.setTexture(*spritesheet->texture);
}

// That's it ! Now the animation is set up, 
// we only have to update the AnimPlayer and the Sprite
void Game::update() {
    this->anim_player.update(this->sprite);
}

AnimPlayer has multiple methods you can use : AnimPlayer::stop, AnimPlayer::pause, AnimPlayer::resume, AnimPlayer::isPlaying, AnimPlayer::setPlaySpeed. Check the documentation for more details.

The process of manually creating and updating an AnimPlayer for each sprite can be tedious and repetitive. ns::ecs::SpriteComponent automates that task. Please look into ns::BaseEntity and ns::ecs::SpriteComponent documentation.