从精灵表中以 sfml 动画精灵

Posted

技术标签:

【中文标题】从精灵表中以 sfml 动画精灵【英文标题】:Animating sprites in sfml from a sprite sheet 【发布时间】:2019-03-10 08:30:16 【问题描述】:

我正在尝试为 sfml 中的精灵制作动画。目前,我可以移动精灵并在朝不同方向移动时更改其图像,但我想在它移动时为其设置动画。我在想可能有一种方法可以使用 sf::Clock 来实现这一点,或者可能有更好的方法。所有精灵都在同一个精灵表上,所以我只需要找到一种方法来根据时间更改 textureRect 的 X 和 Y 坐标,同时在一个方向上移动。如果我遗漏了什么或您有任何问题,我将尽我所能回答。

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();
    


字符.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();

;

字符.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;
    


【问题讨论】:

Maverick 的回答是正确的,但如果您不想推出自己的动画类,您可以使用在 SFML 之上构建的 Thor 库:bromeon.ch/libraries/thor 请记住,Thor 非常有限,并且已知会崩溃。维护者不在附近修复它们。 酷,如果 Mavericks 的答案不适合我,我会检查一下,但我相信它会的,谢谢。 @DapperDaniel 嘿,我不知道这是否是正确的询问方式或地点,但我正在尝试完成您在发布之前已经完成的工作(在更改图片)。我试图通过你的代码,但我不明白你是怎么做到的。有什么办法解释吗?我应该发个帖子或其他东西然后在这里链接还是? 【参考方案1】:

您需要跟踪动画中的帧(sf::IntRects 列表)。并且在两者之间有某种延迟。更新时,只需在框架中移动并应用矩形。

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
          
     
;

你可以这样使用:

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);

【讨论】:

谢谢,我会尽快实现。 要在我当前的代码中实现这一点,我是否只需在 if(sf::Keyboard::isKeyPressed) 下添加我需要的帧,这样当我向不同方向移动时它会播放不同的动画?而且animation.update还是会进入主循环或者键盘输入之后? 在场景开始时不添加帧。如果您需要不同的动画,请使用不同的动画对象,并且只在您需要的那个上调用.update() 在更新参数中,经过的时间是帧之间的时间还是动画开始的时间,我需要在那里使用 sf::Clock 吗? elapsed 是从主循环中的 sf::Clock 返回的秒数

以上是关于从精灵表中以 sfml 动画精灵的主要内容,如果未能解决你的问题,请参考以下文章

多个动画的动画偏移

精灵动画从左到右,我应该选择啥?动态的还是运动的?

创建智能和快速的精灵表

具有阻尼效果的快速动画精灵

在Unity中使用不带动画的动画制作多个精灵

动画精灵与碰撞检测