Search code examples
c++animationsfmlthor

thor::Animator playing only one frame


I've been trying to animate an sf::Sprite with Thor 2.1, but as of now my sprite only displays one frame of each animation, not going through all of them.

I've got a sample spritesheet out of one of SFML's tutorials for spritesheets:

Test spritesheet

My code (schematic code, not the whole):

#include<SFML/Window.hpp>
#include<SFML/Audio.hpp>
#include<SFML/Graphics.hpp>
#include<SFML/System.hpp>
#include<SFML/Main.hpp>
#include<stdlib.h>
#include<iostream>
#include <Thor/Animations.hpp>

int main()
{

sf::Texture texPlayer;
texPlayer.create(96, 128);
if (!texPlayer.loadFromFile(workingDirectory + "\\textures\\animtest.png"))
{
    std::cout << "can't load" << std::endl;
}

sf::Sprite sprPlayer;
sprPlayer.setTexture(texPlayer);

thor::FrameAnimation playerDown;
playerDown.addFrame(10.0f, sf::IntRect(0 , 0, 32, 32));
playerDown.addFrame(10.0f, sf::IntRect(32, 0, 32, 32));
playerDown.addFrame(10.0f, sf::IntRect(64, 0, 32, 32));

thor::FrameAnimation playerLeft;
playerLeft.addFrame(20.0f, sf::IntRect(0 , 32, 32, 32));
playerLeft.addFrame(20.0f, sf::IntRect(32, 32, 32, 32));
playerLeft.addFrame(20.0f, sf::IntRect(64, 32, 32, 32));

thor::FrameAnimation playerRight;
playerRight.addFrame(30.0f, sf::IntRect(0 , 64, 32, 32));
playerRight.addFrame(30.0f, sf::IntRect(32, 64, 32, 32));
playerRight.addFrame(30.0f, sf::IntRect(64, 64, 32, 32));

thor::FrameAnimation playerUp;
playerUp.addFrame(40.0f, sf::IntRect(0 , 96, 32, 32));
playerUp.addFrame(40.0f, sf::IntRect(32, 96, 32, 32));
playerUp.addFrame(40.0f, sf::IntRect(64, 96, 32, 32));

thor::AnimationMap<sf::Sprite, std::string> playerAMap;
playerAMap.addAnimation("down" , playerDown , sf::seconds(2.0f));
playerAMap.addAnimation("left" , playerLeft , sf::seconds(3.0f));
playerAMap.addAnimation("right", playerRight, sf::seconds(4.0f));
playerAMap.addAnimation("up"   , playerUp   , sf::seconds(5.0f));

thor::Animator<sf::Sprite, std::string> playerAnimator = thor::Animator<sf::Sprite, 
std::string>::Animator(playerAMap);

sf::Clock animFrameTime;

sf::RenderWindow window;
window.create(sf::VideoMode(800, 450), "Placeholder", sf::Style::Default);

while (window.isOpen())
{

// here's movement direction checking, event handling, dealing with movement overall

// let's say we dont use any of that now and we want to play just the "up" animation

playerAnimator.play() << "up" << thor::Playback::loop("up");

playerAnimator.update(animFrameTime.restart());
playerAnimator.animate(sprPlayer);

window.draw(sprPlayer);
}

return 0;
}

As I said, the behaviour of that code is it just plays the first frame of up animation (in this case the upper-left frame of the spritesheet).

For "left" animation it plays the first frame of the second row, and for all the others respectively.


Solution

  • First,

    texPlayer.create(96, 128);
    

    is not needed, as you immediately load a file and thus overwrite the texture. sf::Texture::create() is mainly useful when you edit the texture after loading it, or use it for low-level OpenGL.

    The thor::Animator can be understood to have two modes of operation, each of which covers a few methods:

    • Select/switch animation: play(), stop(), queue()
      This should be invoked only when you want to change the animation playing (a character starts to move/attack) or when you want to stop a playing animation (character stands still).
    • Update state and apply graphics: update(), animate()
      This should be applied every graphics frame. If you want deterministic animation behavior (your animations require constant progress), you should call update() in a fixed-time logic step.

    Those are already all the methods that thor::Animator offers, see official documentation.

    The mistake in your code is that you call play() in every frame, thus restarting the animation constantly. What you should do instead is call play() before the while loop. Since you specified thor::Playback::loop(), the animation will be looping as long as you update the animator.