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

如何解决SFML 3.0中Pong游戏的移动卡顿问题?

Pong游戏移动卡顿问题排查

我的Pong游戏移动时非常卡顿,推测问题出在程序休眠时长的计算方式上。游戏已设置最大帧率为60,且我的电脑性能足以达到该帧率。我参考了《Fix Your Timestep》指南,但不确定是否正确实现。

代码实现

main.cpp

#include <SFML/Graphics.hpp>
#include <iostream>

#include "Paddle.hpp"

int framerateLimit = 60; 
unsigned int width = 800;
unsigned int height = 600;

int main()
{
    sf::RenderWindow window(sf::VideoMode({width, height}), "My window");
    window.setFramerateLimit(framerateLimit);
    
    sf::RectangleShape leftPaddle;
    leftPaddle.setSize(sf::Vector2f(10,50));
    leftPaddle.setPosition(sf::Vector2f(20, height / 2.0 - leftPaddle.getSize().y/2));    
    std::cout << leftPaddle.getPosition().x << "," << leftPaddle.getPosition().y << std::endl;

    sf::RectangleShape rightPaddle;
    rightPaddle.setSize(sf::Vector2f(10, 50));
    rightPaddle.setPosition(sf::Vector2f(width - 20 - leftPaddle.getSize().x, height / 2.0 - leftPaddle.getSize().y / 2));

    sf::Clock clock;
    
    const float deltaTime = 0.01f;
    float accumulator = 0.0f;
    float currentTime = clock.getElapsedTime().asSeconds();
    

    while (window.isOpen())
    {
        
        int64_t startTicks = clock.getElapsedTime().asMilliseconds();

        float newTime = clock.getElapsedTime().asSeconds();
        float frameTime = newTime - currentTime;
        
        currentTime = newTime;

        accumulator += frameTime;
        

        while (accumulator >= deltaTime) {
            while (const std::optional event = window.pollEvent())
            {
                // "close requested" event: we close the window
                if (event->is<sf::Event::Closed>()) {
                    window.close();
                }
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) {
                    Paddle::moveUp(leftPaddle);
                }
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) {
                    Paddle::moveDown(leftPaddle);
                }
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) {
                    Paddle::moveUp(rightPaddle);
                }
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) {
                    Paddle::moveDown(rightPaddle);
                }

            }
            accumulator -= deltaTime;
        }

        const float alpha = accumulator / deltaTime;

        
        window.clear(sf::Color::Black);

        window.draw(leftPaddle);
        window.draw(rightPaddle);

        window.display();

        
        int frameTicks = clock.getElapsedTime().asMilliseconds() - startTicks;

        if (frameTicks < 1000 / framerateLimit) {
            int duration = 1000 / framerateLimit - frameTicks;
            sf::sleep(sf::milliseconds(duration));
        }
        
    }
}

Paddle.cpp

#include "Paddle.hpp"

void Paddle::moveUp(sf::RectangleShape& paddle) {
    paddle.getPosition().y <= 10.f ? paddle.setPosition(sf::Vector2f(paddle.getPosition().x,0)) : paddle.move(sf::Vector2f(0, -10));
}

void Paddle::moveDown(sf::RectangleShape& paddle) {
    paddle.getPosition().y >= height - paddle.getSize().y - 10 ? paddle.setPosition(sf::Vector2f(paddle.getPosition().x,height - paddle.getSize().y)) : paddle.move(sf::Vector2f(0, 10));

}

问题根源

  1. 输入处理嵌套错误:键盘输入检测放在了固定时间步循环内,当accumulator包含多个deltaTime时,同一帧内会重复执行移动逻辑,导致 paddle 多次位移,移动速度忽快忽慢,视觉上卡顿。
  2. 重复帧率控制:同时使用window.setFramerateLimit(60)和手动计算休眠时长,两者冲突,导致帧率不稳定。
  3. 移动逻辑无时间绑定:每次移动固定10像素,帧率波动时移动速度随帧率变化,造成卡顿感。
  4. 事件循环重复执行:在固定时间步循环内调用pollEvent,同一帧内多次处理事件,可能引发异常行为。

修复方案

1. 调整输入与事件处理流程

将事件处理和键盘输入检测移出固定时间步循环,确保每一帧只处理一次输入:

while (window.isOpen())
{
    // 先处理所有窗口事件
    while (const std::optional event = window.pollEvent())
    {
        if (event->is<sf::Event::Closed>()) {
            window.close();
        }
    }

    // 每一帧只检测一次键盘状态
    bool leftUp = sf::Keyboard::isKeyPressed(sf::Keyboard::W);
    bool leftDown = sf::Keyboard::isKeyPressed(sf::Keyboard::S);
    bool rightUp = sf::Keyboard::isKeyPressed(sf::Keyboard::Up);
    bool rightDown = sf::Keyboard::isKeyPressed(sf::Keyboard::Down);

    // 时间步计算
    float newTime = clock.getElapsedTime().asSeconds();
    float frameTime = newTime - currentTime;
    currentTime = newTime;

    accumulator += frameTime;

    // 固定时间步更新游戏逻辑
    while (accumulator >= deltaTime) {
        if (leftUp) Paddle::moveUp(leftPaddle, deltaTime);
        if (leftDown) Paddle::moveDown(leftPaddle, deltaTime);
        if (rightUp) Paddle::moveUp(rightPaddle, deltaTime);
        if (rightDown) Paddle::moveDown(rightPaddle, deltaTime);

        accumulator -= deltaTime;
    }

    // 渲染
    window.clear(sf::Color::Black);
    window.draw(leftPaddle);
    window.draw(rightPaddle);
    window.display();
}

2. 修改移动逻辑为时间驱动

让移动速度基于时间步,确保不同帧率下移动速度一致:

// 在Paddle.hpp中添加常量定义
const float PADDLE_SPEED = 300.0f; // 每秒移动300像素
extern unsigned int height;

// Paddle.cpp修改为
void Paddle::moveUp(sf::RectangleShape& paddle, float deltaTime) {
    float moveDist = PADDLE_SPEED * deltaTime;
    float newY = paddle.getPosition().y - moveDist;
    paddle.setPosition(paddle.getPosition().x, std::max(newY, 0.0f));
}

void Paddle::moveDown(sf::RectangleShape& paddle, float deltaTime) {
    float moveDist = PADDLE_SPEED * deltaTime;
    float newY = paddle.getPosition().y + moveDist;
    float maxY = height - paddle.getSize().y;
    paddle.setPosition(paddle.getPosition().x, std::min(newY, maxY));
}

3. 移除重复的休眠代码

删除手动计算休眠的代码块,仅保留window.setFramerateLimit(60)即可,SFML的帧率控制足够稳定。

4. 优化时间初始化

确保currentTime在进入循环前正确初始化(原代码已正确实现,无需修改)。

内容的提问来源于stack exchange,提问作者Omar Khaled

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:50:54