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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 06:15:01