Ok.
Ok.

Reputation: 21

How to make player animation in sfml?

i wanted to make player a bit realistic instead of just hovering around like a ghost. this is my code just some basic gravity and movement, i want to make jumping animation, walking animation, turn left and turn right animation. how do i do that?

here code:

void Game::initTexture()
{
    if (!texture.loadFromFile("/home/webmaster/Documents/vscode-sfml/src/Ball_and_Chain_Bot/pixil-frame-0(1).png"))
    {
        std::cout << "failed to load texture\n";
        rect.setTexture(texture);
        rect.setPosition(this->window->getView().getCenter());
        rect.setScale(2,2);
    }
}

rendering:

void Game::render()
{
    this->window->clear(sf::Color(239, 235, 216));
    this->window->draw(rect);
    this->window->display();
}

Upvotes: 0

Views: 824

Answers (1)

Botje
Botje

Reputation: 30830

You forgot to show your actual code, the headers are not very useful. Regardless:

  1. Define some animations. Each animation should have a target length and a number of frames that cover that target. At the beginning it is easiest to make every frame be equally long, but nobody stops you from having frame 1 take 0.2s, frame 2 0.8s, and frame 3 0.15s.
  2. add code to keep track of a "current animation" that properly cycles through the frames on the proper timescale (ie show each frame for 0.25s if you have 4 frames and a target of 1s). Some animations may cycle, such as the "running" or "idle" animation. A common technique for storing animations is a "texture atlas" that contains all frames of an animation. You can then use sf::Shape::setTextureRect to select a part of the texture to draw.
  3. update your movement and input code to change the animation if the state of the character changes

Let us define the frames of an animation in terms of sf::IntRect sections of a given sprite sheet: (example sprite sheet)

std::vector<sf::IntRect> idle {
  {0, 0, 35, 61}
};
std::vector<sf::IntRect> runningRight {
  {657, 473, 43, 52}, // first frame is at 657x473 and is 43x52 pixels
  // other frames.
};

We can define an Animation class with the following data and methods:

class Animation {
  public:
  Animation(const std::vector<sf::IntRect>& frames, float duration, bool cycles = true) : frames(frames), frameTime(duration / frames.size()), cycles(cycles) { reset(); }

  void reset() {
    currentFrame = 0;
    currentFrameTime = 0;
  }
  void update(float dt);
  const sf::IntRect& getCurrentRect() const { return frames[currentFrame]; }

  private:
  const std::vector<sf::IntRect>& frames;
  const float frameTime;
  bool cycles;
  int currentFrame;
  float currentFrameTime;
};

This implements most of step 2: keeping track of which frame should be on screen, assuming update(dt) is called every frame.

Now all that remains is the update method:

void Animation::update(float dt) {
  currentFrameTime += dt;
  // TODO: take `cycles` into account.
  while (currentFrameTime >= frameTime) {
    currentFrameTime -= frameTime;
    currentFrame = (currentFrame + 1) % frames.size();
  }
}

Finally, to hook this up, create the following variables:

sf::Texture textureAtlas = ...;
Animation currentAnimation{idle, 10.0f};
sf::Sprite player(textureAtlas, currentAnimation.getCurrentRect());
  • In your game's update() code, call currentAnimation.update(dt).
  • In the render function, make sure to call player.setTextureRect(currentAnimation.getCurrentRect()).
  • If you receive input, do something like currentAnimation = Animation{runningRight, 1.0f};

Upvotes: 1

Related Questions