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); };
问题根源与修复方案
核心问题点
- 赋值运算符误用为比较运算符:在
Player::updateMovement()的跳跃判断中,if (velocity.y = 0.f && canJump)使用了=(赋值)而非==(比较),导致每次都会将velocity.y设为0,且条件逻辑完全失效。 - 跳跃逻辑未正确包裹:即使修复比较运算符,后续设置速度和
canJump的代码不在if代码块内,意味着只要按下跳跃键就会执行,空中也能触发。 canJump未初始化:Player::initPhysics()中没有给canJump设置初始值true,默认值随机,导致逻辑不稳定。- 按键检测方式错误:用
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
相关产品推荐
相关产品推荐

