You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SFML动画仅播放单帧:按键触发三帧循环实现求助

问题描述

希望实现按下按键时,动画循环切换三张图片。目前存在两个问题:

  1. 仅在按键按下时切换帧,否则动画不在屏幕显示;
  2. 尝试更新动画后,动画卡在某一帧无法循环。

尝试过增加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 19:20:35