C+++Raylib开发2D游戏:按键释放后Timer持续移动功能失效
问题描述
我用C++和Raylib开发2D游戏原型,核心需求是:释放移动按键(比如KEY_RIGHT)后,玩家角色要持续移动3秒,但这个功能没法正常工作。我怀疑是指针的问题,查了资料也没找到错误,附上相关代码求排查。
main.cpp
我加了调试打印,但还是找不到问题。
#include <raylib.h> #include "player.h" #include "timer1.h" #include <iostream> int main() { const int screenWidth = 800; const int screenHeight = 600; InitWindow(screenWidth, screenHeight, "Joj"); SetTargetFPS(60); Player player(50, 50, 20, 20, YELLOW); GameTimer timer; while (!WindowShouldClose()) { std::cout << "Timer duration: " << timer.timerDuration << "\n"; if (timer.timerDuration>0) std::cout << "Timer is on!"; player.Movement(); if (timer.runTimer) { timer.StartTimer(3); std::cout << "Timer is on?"; } std::cout << "Timer value:" << timer.runTimer << "\n"; timer.RunTimer(); BeginDrawing(); ClearBackground(GRAY); player.Draw(); EndDrawing(); } CloseWindow(); return 0; }
timer1.h
我知道把属性设为公有不符合封装规范,只是为了排查问题才这么做的。
#pragma once #include <raylib.h> class GameTimer { public: GameTimer(); void StartTimer(float time); bool RunTimer(); bool *prunTimer; bool runTimer; float timerDuration; float* timerptr; int framesCounter; private: };
timer1.cpp
Timer通过StartTimer()和RunTimer()两个方法工作:StartTimer()把时间参数赋值给指向timerDuration的指针,只设置时长;RunTimer()通过扣除帧时间实现稳定计时。
#include "timer1.h" #include <iostream> GameTimer::GameTimer() { prunTimer = &runTimer; } void GameTimer::StartTimer(float time) { timerptr = &timerDuration; *timerptr = time; *prunTimer = false; } bool GameTimer::RunTimer() { if (timerDuration>0) { *timerptr -= GetFrameTime(); return false; } else { std::cout << "timerDuration is less than or equal to 0" << "\n"; return true; } }
player.h
我还有个Entity基类,包含Draw()等基础方法,这里暂时不相关。
#include "entity.h" class Player : public Entity { public: using Entity::Entity; void Movement(); private: int leftMovement; int rightMovement; };
player.cpp
#include "player.h" #include "timer1.h" #include <iostream> #define HSPEED 2 void Player::Movement() { GameTimer timer; if (IsKeyDown(KEY_RIGHT)) { rightMovement = getPosX() + HSPEED; setPosX(rightMovement); } else if (IsKeyReleased(KEY_RIGHT)) { std::cout << "released!" << "\n"; *timer.prunTimer = true; std::cout << "runTimer value: " << timer.runTimer << "\n"; } if (timer.timerDuration>0) { rightMovement = getPosX() + HSPEED; setPosX(rightMovement); } std::cout << "Runtimer after: " << timer.runTimer; }
问题排查与修复
核心问题1:Player内的GameTimer是局部变量
Player::Movement()里定义的GameTimer timer是局部变量,每次调用Movement()都会创建新实例,之前设置的状态直接丢失。释放按键时设置的runTimer仅在当前帧有效,下一帧就被重置,根本无法持续计时。
核心问题2:main与Player的Timer是独立实例
main函数里的GameTimer timer和Player内部的timer完全无关,main里的Timer逻辑无法作用到Player的移动控制上,两者状态完全脱节。
核心问题3:Timer逻辑混乱且冗余
- StartTimer里把
runTimer设为false,和启动计时的需求逻辑相反; - 多余的指针变量(prunTimer、timerptr)完全没必要,直接操作成员变量即可,指针反而增加出错概率;
- RunTimer的返回值设计和实际状态更新不匹配。
修复步骤
1. 让Player共享全局Timer实例
修改Player类,通过引用接收main中的Timer实例,保证状态统一:
修改player.h
#include "entity.h" #include "timer1.h" class Player : public Entity { public: using Entity::Entity; void Movement(GameTimer& timer); // 改为接收Timer引用 private: int leftMovement; int rightMovement; };
修改player.cpp
#include "player.h" #include "timer1.h" #include <iostream> #define HSPEED 2 void Player::Movement(GameTimer& timer) { if (IsKeyDown(KEY_RIGHT)) { rightMovement = getPosX() + HSPEED; setPosX(rightMovement); timer.StartTimer(0); // 按住按键时重置计时 } else if (IsKeyReleased(KEY_RIGHT)) { std::cout << "released!" << "\n"; timer.StartTimer(3); // 释放按键时启动3秒计时 } if (timer.GetDuration() > 0) { rightMovement = getPosX() + HSPEED; setPosX(rightMovement); } }
2. 重构GameTimer,去掉冗余指针并修正逻辑
修改timer1.h
#pragma once #include <raylib.h> class GameTimer { public: GameTimer(); void StartTimer(float time); void UpdateTimer(); // 改名更清晰,负责更新计时 float GetDuration() const; // 提供接口获取时长,遵循封装原则 private: float timerDuration; // 仅保留必要成员变量 };
修改timer1.cpp
#include "timer1.h" #include <iostream> GameTimer::GameTimer() : timerDuration(0.0f) {} void GameTimer::StartTimer(float time) { timerDuration = time; // 直接赋值,去掉冗余指针 } void GameTimer::UpdateTimer() { if (timerDuration > 0) { timerDuration -= GetFrameTime(); if (timerDuration < 0) timerDuration = 0; // 防止计时出现负数 } } float GameTimer::GetDuration() const { return timerDuration; }
3. 调整main.cpp,传递Timer引用给Player
#include <raylib.h> #include "player.h" #include "timer1.h" #include <iostream> int main() { const int screenWidth = 800; const int screenHeight = 600; InitWindow(screenWidth, screenHeight, "Joj"); SetTargetFPS(60); Player player(50, 50, 20, 20, YELLOW); GameTimer timer; while (!WindowShouldClose()) { player.Movement(timer); // 传递Timer引用 timer.UpdateTimer(); // 每帧更新计时 BeginDrawing(); ClearBackground(GRAY); player.Draw(); EndDrawing(); } CloseWindow(); return 0; }
额外优化建议
- 严格遵循封装原则,不要暴露类的成员变量,通过接口访问;
- 可以给Player添加移动方向状态(如
bool isMovingRight),方便扩展左右两个方向的持续移动逻辑; - 去掉调试用的打印语句,避免影响性能。
内容的提问来源于stack exchange,提问作者Mark11gamer
相关产品推荐
相关产品推荐

