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

SFML+C++游戏无限跳跃Bug排查求助(附完整代码)

问题:SFML游戏中玩家可在空中重复跳跃

我用C++结合SFML开发游戏,现在遇到一个问题:玩家跳跃时能在空中重复触发跳跃,不符合预期。我怀疑问题出在bool变量或碰撞检测逻辑上,但没法确定具体原因。


Game.cpp

#include "Game.h"


//Private Functions


void Game::initPlayer()
{
    this->player = new Player();
}

void Game::initPlatform()
{
    sf::RectangleShape platform(sf::Vector2f(200.f, 20.f));
    platform.setPosition(300.f, 500.f);
    platform.setFillColor(sf::Color::White);
}

void Game::initVariables()
{
    
}

void Game::initWindow()
{
    this->window.create(sf::VideoMode(800,600), "CFFA Window", sf::Style::Titlebar | sf::Style::Close);
    this->window.setFramerateLimit(144);
     

}

//Constructors 

Game :: Game()
{
    this->initVariables();
    this->initWindow();
    this->initPlayer();

}

Game ::~Game()
{
    
    delete this->player;
}

const bool Game::running() const
{
    return this->window.isOpen();
}

//Accessors

//Functions

void Game::render()
{
    /*
        return void

        - clears the old frame
        - renders the new objects
        - displays the frame in the new window

        Renders the game objects
    */

    this->window.clear();

    //Draw game objects
    this->renderPlayer();

    this->window.display();
}

void Game::update()
{
    //Event Polling
    while (this->window.pollEvent(this->ev))
    {
        if (this->ev.type == sf::Event::Closed)
        {
            this->window.close();
        }
        else if (this->ev.type == sf::Event::KeyPressed && this->ev.key.code == sf::Keyboard::Escape)
        { 
            this->window.close();
        }
        if (this->ev.type == sf::Event::KeyReleased &&
            (this->ev.key.code == sf::Keyboard::D ||
                this->ev.key.code == sf::Keyboard::A ||
                this->ev.key.code == sf::Keyboard::W ||
                this->ev.key.code == sf::Keyboard::S
                )
            ) // Need to Document when animation fixed

            this->player->resetAnimationTimer();
    }
    
    this->updatePlayer();
}

void Game::renderPlayer()
{
    this->player->render(this->window);
}

const sf::RenderWindow & Game::getWindow() const
{
    // TODO: insert return statement here
    return this->window;
}

void Game::updatePlayer()
{
    this->player->update();
    this->updateCollision();
}

void Game::updateCollision()
{
    //Collision with Window
    if (this->player->getPosition().y + this->player->getGlobalBounds().height > this->window.getSize().y)
    {
        this->player->setCanJump(true);
        this->player->resetVeloictyY();
        this->player->setPosition(this->player->getPosition().x, this->window.getSize().y - this->player->getGlobalBounds().height);
    }
    /*
    if (this->player->getPosition().x + this->player->getGlobalBounds().width > this->window.getSize().x)
    {
        this->player->resetVelocityX();
        this->player->setPosition(this->window.getSize().x - this->player->getGlobalBounds().width, this->player->getPosition().y);
    }
    if (this->player->getPosition().x < 0)
    {
        this->player->resetVelocityX();
        this->player->setPosition(0, this->player->getPosition().y);
    }
    */
    //Check collision between objects
    
}

Player.h

#pragma once
#include "Precomp.h"



enum PLAYER_ANIMATION_STATES { Idle = 0, Left_Move, Right_Move, Jumping };

class Player
{

private:
    sf::Sprite sprite;
    sf::Texture textureSheet;
    sf::Clock animationTimer;
    
    //Animation
    short animationStates;
    sf::IntRect currentFrame;
    bool animationSwitch;


    // Player Physics 
    sf::Vector2f velocity;
    float maxVelocity;
    float minVelocity;
    float acceleration;
    float drag;
    float gravity;
    float MaxYVelocity;
    float jumpVelocity;
    bool canJump;
    float jumpTimer;
    
    
    
    
    //Health Points
    
    //Private functions
    void initVariables();
    void initTexture();
    void initSprite();
    void initAnimations();
    void initPhysics();

public:
    Player();
    virtual ~Player();

    //Accessors 
    const bool& getAnimationSwitch();
    const sf::Vector2f getPosition() const;
    const sf::FloatRect getGlobalBounds() const;

    //Modifiers
    void setPosition(const float x, const float y);
    void resetVeloictyY();
    void setCanJump(const bool canJump) { this->canJump = canJump; }

    //Functions
    void resetAnimationTimer();
    void move(const float dir_x, const float dir_y);
    void updatePhysics();
    void update();
    void updateAnimations();
    void updateMovement();
    void render(sf::RenderTarget& target);
    
};

Player.cpp

#include "Player.h"

void Player::initVariables()
{
    this->animationStates = PLAYER_ANIMATION_STATES::Idle;
}


void Player::initTexture()
{
        //lLoad a texture
        if (!this->textureSheet.loadFromFile("Sprites and Textures/Kim Pine Full Sprite Sheet 3.png"))
        {
            std::cout << "Failed";
        }
}

void Player::initSprite()
{
    //Set the texture to the sprite

    this->sprite.setTexture(this->textureSheet);

    //Resize the sprite
    this->sprite.scale(3.f, 3.f);
    this->currentFrame = sf::IntRect(0, 0 , 48, 95);
    this->sprite.setTextureRect(this->currentFrame);
}

void Player::initAnimations()
{
    this->animationTimer.restart();
    this->animationSwitch = true;
    this->currentFrame.left = 0.f;

}

void Player::initPhysics()
{
    this->maxVelocity = 10.f;
    this->minVelocity = 1.f;
    this->acceleration = 1.05f;
    this->drag = 0.96f;
    this->gravity = 4.f;
    this->MaxYVelocity = -15.f;
    this->jumpVelocity = -12.0f;

}

Player::Player()
{
    this->initVariables();
    this->initTexture();
    this->initSprite();
    this->initAnimations();
    this->initPhysics();

    
}

Player ::~Player()
{

}

const bool& Player::getAnimationSwitch()
{
    bool anim_switch = this->animationSwitch;

    if (this->animationSwitch)
        this->animationSwitch = false;

    return anim_switch; //Have not documented this yet as I have not fixed the animation yet
}

const sf::Vector2f Player::getPosition() const
{
    return this->sprite.getPosition();
}

const sf::FloatRect Player::getGlobalBounds() const
{
    return this->sprite.getGlobalBounds();
}

void Player::setPosition(const float x, const float y)
{
    this->sprite.setPosition(x, y);
}

void Player::resetVeloictyY()
{
    this->velocity.y = 0.f;
}


void Player::updateAnimations()
{
    if (this->animationStates == PLAYER_ANIMATION_STATES::Idle)
    {
        if (this->animationTimer.getElapsedTime().asSeconds() >= 0.25f || this->getAnimationSwitch())
        {
            this->currentFrame.top = 0.f;
            this->currentFrame.left += 49.25f;
            if (this->currentFrame.left >= 147.75f)
                this->currentFrame.left = 0;


            this->animationTimer.restart();
            this->sprite.setTextureRect(this->currentFrame);
        }
    }
    else if (this->animationStates == PLAYER_ANIMATION_STATES::Right_Move)
    {

        if (this->animationTimer.getElapsedTime().asSeconds() >= 0.1f || this->getAnimationSwitch())
        {
            this->currentFrame.top = 92.0f;
            this->currentFrame.left += 49.1f;
            if (this->currentFrame.left >= 245.9f)
                this->currentFrame.left = 0;


            this->animationTimer.restart();
            this->sprite.setTextureRect(this->currentFrame);
            
        }
        this->sprite.setScale(3.f, 3.f);
        this->sprite.setOrigin(0.f, 0.f);
    }
    else if (this->animationStates == PLAYER_ANIMATION_STATES::Left_Move)
    {

        if (this->animationTimer.getElapsedTime().asSeconds() >= 0.1f || this->getAnimationSwitch())
        {
            this->currentFrame.top = 92.0f;
            this->currentFrame.left += 49.1f;
            if (this->currentFrame.left >= 245.9f)
                this->currentFrame.left = 0;


            this->animationTimer.restart();
            this->sprite.setTextureRect(this->currentFrame);
            
        }
        this->sprite.setScale(-3.f, 3.f);
        this->sprite.setOrigin(this->sprite.getGlobalBounds().width / 3.f, 0.f);
    }
    /*
    else if (this->animationStates == PLAYER_ANIMATION_STATES::Jumping)
    {
        if (this->animationTimer.getElapsedTime().asSeconds() >= 0.1f || this->getAnimationSwitch())
        {
            this->currentFrame.top = 196.0f;
            this->currentFrame.left += 59.8f;
            if (this->currentFrame.left >= 778.f)
                this->currentFrame.left = 0;


            this->animationTimer.restart();
            this->sprite.setTextureRect(this->currentFrame);

        }
        this->sprite.setScale(3.f, 3.f);
        this->sprite.setOrigin(0.f, 0.f);
    }
    */
    else
    {
        this->animationTimer.restart();
    }
}

void Player::updateMovement()
{
        //Move Player
        

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Left))
    {
        this->move(-1.f, 0.f);
        

    }
    else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Right))
    {
        this->move(1.f, 0.f);
        

    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Space) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W))
    {
        if (velocity.y = 0.f && canJump)
        {
            this->velocity.y = jumpVelocity;
            jumpTimer = 0.f;
        }
            this->velocity.y = jumpVelocity;
            this->canJump = false;
            this->animationStates = PLAYER_ANIMATION_STATES::Jumping;
            
            if (sf::Event::KeyReleased)
            {
                if (this->velocity.y < -12.f)
                    this->velocity.y = -12.f;
            }

    }
    if (this->velocity.x > 0.f)
    {
        this->animationStates = PLAYER_ANIMATION_STATES::Right_Move;
    }
    else if (this->velocity.x < 0.f)
    {
        this->animationStates = PLAYER_ANIMATION_STATES::Left_Move;
    }
    else
    {
        this->animationStates = PLAYER_ANIMATION_STATES::Idle;
    }
}

void Player::resetAnimationTimer()
{
    this->animationTimer.restart();
    this->animationSwitch = true;
}

void Player::move(const float dir_x, const float dir_y)
{
    //Accceleration
    this->velocity.x += dir_x * this->acceleration;
    

    //Limit Velocity
    if (std::abs(this->velocity.x) > this->maxVelocity)
    {
        this->velocity.x = this->maxVelocity * ((this->velocity.x < 0.f) ? -1.f : 1.f);
    }

}   

void Player::updatePhysics()
{
    //Gravity
    this->velocity.y += gravity * 1.0;

    //Deceleration
    this->velocity *= this->drag;

    //Limit Deceleration
    if (std::abs(this->velocity.x) < this->minVelocity)
        this->velocity.x = 0.f;
    if (std::abs(this->velocity.y) < this->minVelocity)
        this->velocity.y = 0.f;
    if (std::abs(this->velocity.x) <= 1.f)
        this->velocity.x = 0.f;

    this->sprite.move(this->velocity);


}

void Player::update()
{
    this->updateMovement();
    this->updatePhysics();
    this->updateAnimations();
    
}



void Player::render(sf::RenderTarget& target)
{
    target.draw(this->sprite);

    sf::CircleShape circ;
    circ.setFillColor(sf::Color::Red);
    circ.setRadius(2.f);
    circ.setPosition(this->sprite.getPosition());

    target.draw(circ);

};

问题根源与修复方案

核心问题点

  1. 赋值运算符误用为比较运算符:在Player::updateMovement()的跳跃判断中,if (velocity.y = 0.f && canJump)使用了=(赋值)而非==(比较),导致每次都会将velocity.y设为0,且条件逻辑完全失效。
  2. 跳跃逻辑未正确包裹:即使修复比较运算符,后续设置速度和canJump的代码不在if代码块内,意味着只要按下跳跃键就会执行,空中也能触发。
  3. canJump未初始化:Player::initPhysics()中没有给canJump设置初始值true,默认值随机,导致逻辑不稳定。
  4. 按键检测方式错误:用sf::Keyboard::isKeyPressed会在按住按键时持续触发跳跃,应该用KeyPressed事件监听实现单次触发。

修复步骤

1. 初始化canJump

在Player::initPhysics()中添加:

this->canJump = true;

2. 修复跳跃判断逻辑

修改Player::updateMovement()中的跳跃代码:

if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
    // 修复比较运算符,将跳跃逻辑包裹在条件内
    if (velocity.y == 0.f && canJump)
    {
        this->velocity.y = jumpVelocity;
        jumpTimer = 0.f;
        this->canJump = false;
        this->animationStates = PLAYER_ANIMATION_STATES::Jumping;
    }
}

3. 改用事件监听实现单次跳跃(推荐)

在Game::update()的事件循环中添加跳跃事件处理:

while (this->window.pollEvent(this->ev))
{
    // ... 其他事件 ...
    if (this->ev.type == sf::Event::KeyPressed)
    {
        if (this->ev.key.code == sf::Keyboard::Space || this->ev.key.code == sf::Keyboard::Up || this->ev.key.code == sf::Keyboard::W)
        {
            this->player->jump();
        }
    }
}

在Player.h中添加jump方法声明:

void jump();

在Player.cpp中实现jump方法:

void Player::jump()
{
    if (this->canJump)
    {
        this->velocity.y = this->jumpVelocity;
        this->canJump = false;
        this->animationStates = PLAYER_ANIMATION_STATES::Jumping;
    }
}

4. 移除无效代码

删除Player::updateMovement()中无效的`if (sf::Event

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 07:21:50