SFML动画仅播放单帧:按键触发三帧循环实现求助
问题描述
希望实现按下按键时,动画循环切换三张图片。目前存在两个问题:
- 仅在按键按下时切换帧,否则动画不在屏幕显示;
- 尝试更新动画后,动画卡在某一帧无法循环。
尝试过增加current_frame计数器,但导致动画从屏幕消失;尝试刷新精灵则清空了全部内容。
相关代码
Animation.h
#pragma once #include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics/Sprite.hpp> class Animation { bool reversed; unsigned short animation_iterator; unsigned short animation_speed; unsigned short current_frame; unsigned short frame_width; unsigned short total_frames; sf::Sprite sprite; sf::Texture texture; public: Animation(const unsigned short i_frame_width, const std::string& i_texture_location, const unsigned short i_animation_speed = 1); void draw(sf::RenderWindow& i_window); void set_animation_speed(const unsigned short i_animation_speed); void set_reversed(const bool i_value); void set_position(const short i_x, const short i_y); void set_texture_location(const std::string& i_texture_location); void update(); }; Animation::Animation(const unsigned short i_frame_width, const std::string& i_texture_location, const unsigned short i_animation_speed) : reversed(0), animation_iterator(0), animation_speed(0), current_frame(0), frame_width(i_frame_width) { texture.loadFromFile(i_texture_location); total_frames = texture.getSize().x / frame_width; } void Animation::draw(sf::RenderWindow& i_window) { sprite.setTexture(texture); if (0 == reversed) { sprite.setTextureRect(sf::IntRect(current_frame * frame_width, 0, frame_width, texture.getSize().y)); } else { sprite.setTextureRect(sf::IntRect(frame_width * (1 + current_frame), 0, -frame_width, texture.getSize().y)); } i_window.draw(sprite); } void Animation::set_animation_speed(const unsigned short i_animation_speed) { animation_speed = i_animation_speed; } void Animation::set_reversed(const bool i_value) { reversed = i_value; } void Animation::set_position(const short i_x, const short i_y) { sprite.setPosition(i_x, i_y); } void Animation::set_texture_location(const std::string& i_texture_location) { texture.loadFromFile(i_texture_location); } void Animation::update() { while (animation_iterator >= animation_speed) { animation_iterator -= animation_speed; current_frame = (1 + current_frame) % total_frames; } animation_iterator++; }
Hero.h
#pragma once #include <SFML/Graphics.hpp> #include "Animation.h" int CELL_SIZE = 85; class Hero { bool crouching=0; bool dead=0; bool reversed=0; bool on_ground=0; float horizontal_speed; float vertical_speed; float x=0; float y=0; unsigned char jump_timer=0;//при зажатии кнопки прыжка персонаж прыгает выше sf::Sprite sprite; sf::Texture texture; Animation walk_animation = Animation(CELL_SIZE, "Resources/Images/Hero_Walk.png", 1); public: Hero() { texture.loadFromFile("Resources/Images/Hero_Idle.png"); sprite.setTexture(texture); } void draw(sf::RenderWindow& i_window) { sprite.setPosition(round(x), round(y)); i_window.draw(sprite); if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { sprite.setTextureRect(sf::IntRect(0, 0, texture.getSize().x, texture.getSize().y)); walk_animation.set_position(round(x), round(y)); walk_animation.draw(i_window); } } };
解决方案
1. 修复Animation类构造函数的动画速度初始化错误
原构造函数中animation_speed被初始化为0,导致update()里的循环逻辑完全失效。修改构造函数的初始化列表,把传入的i_animation_speed赋值给成员变量:
Animation::Animation(const unsigned short i_frame_width, const std::string& i_texture_location, const unsigned short i_animation_speed) : reversed(0), animation_iterator(0), animation_speed(i_animation_speed), // 替换原有的0为传入的参数 current_frame(0), frame_width(i_frame_width) { texture.loadFromFile(i_texture_location); total_frames = texture.getSize().x / frame_width; }
2. 给Hero类添加状态管理与update逻辑
当前Hero类没有区分“行走”和“ idle”状态,且没有在每一帧更新动画。需要添加状态标记和更新方法:
#pragma once #include <SFML/Graphics.hpp> #include "Animation.h" int CELL_SIZE = 85; class Hero { bool crouching=0; bool dead=0; bool reversed=0; bool on_ground=0; bool is_walking = false; // 添加行走状态标记 float horizontal_speed; float vertical_speed; float x=0; float y=0; unsigned char jump_timer=0;//按住跳跃键时角色跳得更高 sf::Sprite idle_sprite; // 重命名为idle_sprite,区分行走动画 sf::Texture idle_texture; Animation walk_animation = Animation(CELL_SIZE, "Resources/Images/Hero_Walk.png", 3); // 调整速度为3,避免动画过快 public: Hero() { idle_texture.loadFromFile("Resources/Images/Hero_Idle.png"); idle_sprite.setTexture(idle_texture); walk_animation.set_position(round(x), round(y)); // 初始化行走动画位置 } // 添加update方法,在游戏主循环每帧调用 void update() { // 更新行走状态 is_walking = sf::Keyboard::isKeyPressed(sf::Keyboard::Right); // 如果处于行走状态,更新动画帧和位置 if (is_walking) { walk_animation.update(); walk_animation.set_position(round(x), round(y)); // 可在此添加角色移动逻辑,比如 x += 1.0f; } } void draw(sf::RenderWindow& i_window) { if (is_walking) { // 行走时绘制行走动画 walk_animation.draw(i_window); } else { // 否则绘制idle状态 idle_sprite.setPosition(round(x), round(y)); i_window.draw(idle_sprite); } } };
3. 在游戏主循环中调用Hero的update方法
确保每帧先更新状态和动画,再进行绘制:
int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "Game"); Hero hero; while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(); hero.update(); // 每帧先更新状态与动画 hero.draw(window); // 再绘制角色 window.display(); } return 0; }
关键说明
- 动画必须在**每帧都调用update()**才能正常循环,而非仅在按键按下时触发
- 通过状态变量区分idle和行走状态,避免同时绘制多个精灵导致重叠
- 修复Animation的速度初始化后,帧切换逻辑才能按设定速度工作,保证动画循环正常
内容的提问来源于stack exchange,提问作者Darth Vader
相关产品推荐
相关产品推荐

