Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you animate a sprite in SFML

Lets say I have 4 images and I want to use these 4 images to animate a character. The 4 images represent the character walking. I want the animation to repeat itself as long as I press the key to move but to stop right when I unpress it. It doesn't need to be SFML specific if you don't know it, just basic theory would really help me.

Thank you.

like image 793
DogDog Avatar asked Apr 21 '10 16:04

DogDog


People also ask

How do I display a sprite in SFML?

Setting up a Sprite is generally done in two steps with SFML: first, we load in a texture, then we create a sprite and pass in the texture. Once we have the sprite set up, we can set attributes such as the size and position and then display it by drawing it into the window during the run loop.


2 Answers

You may want some simple kind of state machine. When the key is down (see sf::Input's IsKeyDown method), have the character in the "animated" state. When the key is not down, have the character in "not animated" state. Of course, you could always skip having this "state" and just do what I mention below (depending on exactly what you're doing).

Then, if the character is in the "animated" state, get the next "image" (see the next paragraph for more details on that). For example, if you have your images stored in a simple 4 element array, the next image would be at (currentIndex + 1) % ARRAY_SIZE. Depending on what you are doing, you may want to store your image frames in a more sophisticated data structure. If the character is not in the "animated" state, then you wouldn't do any updating here.

If your "4 images" are within the same image file, you can use the sf::Sprite's SetSubRect method to change the portion of the image displayed. If you actually have 4 different images, then you probably would need to use the sf::Sprite's SetImage method to switch the images out.

like image 140
pikejd Avatar answered Oct 07 '22 14:10

pikejd


How would you enforce a framerate so that the animation doesn't happen too quickly?

Hello please see my answer here and accept this post as the best solution.

https://stackoverflow.com/a/52656103/3624674

You need to supply duration per frame and have the total progress be used to step through to the frame.

In the Animation source file do

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) {
        // increase the total progress of the animation
        totalProgress += elapsed;

        // use this progress as a counter. Final frame at progress <= 0
        double progress = totalProgress;
        for(auto frame : frames) {
           progress -= (*frame).duration;  

          // When progress is <= 0 or we are on the last frame in the list, stop
          if (progress <= 0.0 || &(*frame) == &frames.back())
          {
               target->setTextureRect((*frame).rect);  
               break; // we found our frame
          }
     }
};

To stop when you unpress, simply only animate when the key is held

if(isKeyPressed) {
    animation.update(elapsed); 
}

To support multiple animations for different situations have a boolean for each state

bool isWalking, isJumping, isAttacking;

...

if(isJumping && !isWalking && !isAttacking) {
   jumpAnimation.update(elapsed);
} else if(isWalking && !isAttacking) {
   walkAnimation.update(elapsed);
} else if(isAttacking) { 
   attackAnimation.update(elapsed);
}

...

// now check for keyboard presses
if(jumpkeyPressed) { isJumping = true; } else { isJumping false; }
like image 34
Dan Avatar answered Oct 07 '22 15:10

Dan