-
Notifications
You must be signed in to change notification settings - Fork 5
Spritesheet and Animation
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):
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);
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);
this->sprite.setTextureRect(this->anim_player.getActiveFrame().rectangle);
}
// 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.setTextureRect(this->anim_player.getActiveFrame().rectangle);
}
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.