1

I am trying to animate a sprite in sfml. At the moment I am able to move the sprite and change it's image when moving in a different direction, but I want to animate it while it is moving. I am thinking that there might be a way to accomplish this with sf::Clock or there might be a better way. All sprites are on the same sprite sheet so I just need to find a way to change the X and Y coordinates of the textureRect based on a time while moving in a direction. If I am missing something or you have any questions, I will answer to the best of my ability.

main.cpp

#include <iostream>
#include <SFML/Graphics.hpp>
#include "Character.hpp"

int main() {
    sf::RenderWindow window(sf::VideoMode(5000, 5000), "Awesome Game" );
    Character Boi("SpritesBoi.png", 0, 0, 5, 100);
    sf::Sprite BoiSprite = Boi.getSprite();
    Boi.SheetX = 0;
    Boi.SheetY = 48;

    while (window.isOpen()){

        // check all the window's events that were triggered since the last iteration of the loop
        sf::Event event;
        while (window.pollEvent(event)){
            // "close requested" event: we close the window
            if (event.type == sf::Event::Closed){
                window.close();
            }
        }

        Boi.Move();
        BoiSprite.setTextureRect(sf::IntRect(Boi.SheetX, Boi.SheetY, 110, 150));
        BoiSprite.setPosition(Boi.x_pos, Boi.y_pos);
        window.clear(sf::Color(255, 255, 255));
        window.draw(BoiSprite);
        window.display();
    }

}

Character.hpp

#ifndef Character_hpp
#define Character_hpp

#include <stdio.h>
#include <SFML/Graphics.hpp>
#endif /* Character_hpp */

class Character{
public:
    int health;
    int speed;
    int x_pos;
    int y_pos;
    int SheetX;
    int SheetY;
    sf::Texture texture;
    sf::Sprite sprite;

    Character(std::string image, int xlocation, int ylocation, int s, int h){
        health = h;
        speed = s;
        x_pos = xlocation;
        y_pos = ylocation;
        texture.loadFromFile(image);
    }
    sf::Sprite getSprite() {
        sprite.setTexture(texture);
        sprite.setPosition(x_pos, y_pos);
        sprite.setTextureRect(sf::IntRect(SheetX, SheetY, 110, 150));
        return sprite;
    }

    void Move();

};

Character.cpp

#include "Character.hpp"
#include <iostream>
void Character::Move(){



    //Up
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)){
        SheetX = 0;
        SheetY = 192;
        y_pos = y_pos - 1;
        Up = true;

    }
    //Down
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)){
        SheetX = 0;
        SheetY = 48;
        y_pos = y_pos + 1;
        Down = false;

    }
    //Left
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
        SheetX = 0;
        SheetY = 480;
        x_pos = x_pos - 1;
        Left = true;

    }
    //Right
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
        SheetX = 0;
        SheetY = 339;
        x_pos = x_pos + 1;
        Right = true;

    }
    //Up Right
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up) and sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
        SheetX = 334;
        SheetY = 490;

    }
    //Up Left
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up) and sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
        SheetX = 333;
        SheetY = 340;
    }
    //Down Right
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down) and sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
        SheetX = 334;
        SheetY = 48;
    }
    //Down Left
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down) and sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
        SheetX = 334;
        SheetY = 191;
    }

}
DapperDaniel
  • 75
  • 1
  • 10
  • Maverick's answer is correct, but if you'd prefer not to roll your own animation classes you can use the Thor libraries that are built to work on top of SFML: http://www.bromeon.ch/libraries/thor/ – Alex Meuer Oct 05 '18 at 09:46
  • Keep in mind Thor is very limited and has been known to crash. The maintainer isn't around to fix them. – Dan Oct 05 '18 at 12:09
  • Cool, will check this out if Mavericks answer does not work out for me, but I believe it will, thanks. – DapperDaniel Oct 06 '18 at 12:32
  • @DapperDaniel Hey, i don't know if this is the right way or place to ask but i am trying to accomplish what you already did even before posting this(moving sprite in different directions while changing the image). I tried to go trough your code but i don't understand how you did it. Any way to explain it? Should i make a post or something then link it here or? – logoslavikus Aug 29 '20 at 17:13

1 Answers1

4

You need to keep track of the frames in the animation (list of sf::IntRects). And have some sort of delay inbetween. On update, simply move through the frames and apply the rectangle.

struct Frame {
   sf::IntRect rect;
   double duration; // in seconds
};

class Animation {
   std::vector<Frame> frames;
   double totalLength;
   double totalProgress;
   sf::Sprite *target;
   public:
     Animation(sf::Sprite& target) { 
       this->target = &target;
       totalProgress = 0.0;
     }

     void addFrame(Frame&& frame) {
       frames.push_back(std::move(frame)); 
       totalLength += frame.duration; 
     }

     void update(double elapsed) {
        totalProgress += elapsed;
        double progress = totalProgress;
        for(auto frame : frames) {
           progress -= (*frame).duration;  

          if (progress <= 0.0 || &(*frame) == &frames.back())
          {
               target->setTextureRect((*frame).rect);  
               break; // we found our frame
          }
     }
};

You can use like so:

sf::Sprite myCharacter;
// Load the image...
Animation animation(myCharacter);
animation.addFrame({sf::IntRect(x,y,w,h), 0.1});
// do this for as many frames as you need

// In your main loop:
animation.update(elapsed);
window.draw(myCharacter);
Dan
  • 1,812
  • 3
  • 13
  • 27
  • Thanks for this, will implement it when I can. – DapperDaniel Oct 05 '18 at 01:52
  • To implement this in my current code, would I just add the frames I need under my if(sf::Keyboard::isKeyPressed) so when I move in different directions it plays different animations? And animation.update will still go in main loop or after the keyboard input? – DapperDaniel Oct 06 '18 at 12:42
  • No add the frames at the start of the scene. If you need different animations use different animation objects and only call `.update()` on the one you need. – Dan Oct 06 '18 at 20:56
  • in the update parameters, is the elapsed time the time between frames or the time from when the animation started, would I need to use sf::Clock there? – DapperDaniel Oct 07 '18 at 00:50
  • elapsed is the time in seconds returned from sf::Clock in your main loop – Dan Oct 07 '18 at 03:48
  • Ah ok I got it working, just a note to whomever else may see this, 0.01 is too fast to see a difference. – DapperDaniel Oct 07 '18 at 16:30
  • Great! Yes I use animations in my games that have many frames. 0.1 should have been the sweet spot. – Dan Oct 07 '18 at 18:54