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

Raylib相机异常低头Bug求助:非target代码引发视角下移

Raylib相机缓慢低头/位置下移Bug的优化解决方案

问题描述

在Raylib项目中实现地板高度限制(相机不低于y=2.0)和跳跃功能时,出现相机缓慢低头的异常:即使未修改camera.target,相机position.y仍会逐渐降到1.999...左右。目前通过强制重置相机位置到y=2.0临时解决,需要更优方案。

核心问题代码

跳跃逻辑

if (IsKeyDown(KEY_SPACE)) {
       if (camera.position.y <= 2.0f) {
             yMomentum = yMomentum + speed;
       }
}

临时修复代码

// 强制相机位置高于0
if (camera.position.y < 2.0f) {
    camera.position.y = 2.0f;
    yMomentum = 0;
}

完整项目代码

#include "raylib.h"
#include "rcamera.h"

#define MAX_COLUMNS 20

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
    // 初始化
    //--------------------------------------------------------------------------------------
    const int screenWidth = 1280;
    const int screenHeight = 720;

    InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera first person");

    // 定义相机参数(位置、目标点、上方向向量)
    Camera camera = { 0 };
    camera.position = (Vector3){ 0.0f, 2.0f, 0.0f };    // 相机位置
    camera.target = (Vector3){ 1.0f, 2.0f, 4.0f };      // 相机看向的点
    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // 相机上方向向量
    camera.fovy = 85.0f;                                // 相机垂直视野角度
    camera.projection = CAMERA_PERSPECTIVE;             // 相机投影类型
    float yMomentum = 0.0f;
    int cameraMode = CAMERA_FIRST_PERSON;


    DisableCursor();                    // 限制光标在窗口内相对移动
    void input(float xMomentum);
    //--------------------------------------------------------------------------------------

    // 主游戏循环
    while (!WindowShouldClose())        // 检测窗口关闭按钮或ESC键
    {
        // 根据相机模式自动计算移动,部分默认输入硬编码
        // 高级控制建议手动计算相机移动

        UpdateCameraPro(&camera,
            (Vector3) {
                0.0f,   // 前后移动
                0.0f,   // 左右移动
                yMomentum       // 上下移动
        },
            (Vector3) {
            GetMouseDelta().x * 0.05f,                          // 偏航角旋转
                GetMouseDelta().y * 0.05f,                          // 俯仰角旋转
                0.0f                                                // 滚转角旋转
        },
                0.0f);                              // 缩放(移向目标点)

        //----------------------------------------------------------------------------------

        // 绘制
        //----------------------------------------------------------------------------------
        BeginDrawing();

        ClearBackground(RAYWHITE);

        BeginMode3D(camera);

        DrawPlane((Vector3) { 0.0f, 0.0f, 0.0f }, (Vector2) { 32.0f, 32.0f }, LIGHTGRAY); // 绘制地面
        DrawCube((Vector3) { -16.0f, 2.5f, 0.0f }, 1.0f, 5.0f, 32.0f, BLUE);    // 绘制蓝色墙体
        DrawCube((Vector3) { 16.0f, 2.5f, 0.0f }, 1.0f, 5.0f, 32.0f, LIME);     // 绘制绿色墙体
        DrawCube((Vector3) { 0.0f, 2.5f, 16.0f }, 32.0f, 5.0f, 1.0f, GOLD);     // 绘制黄色墙体


        EndMode3D();

        EndDrawing();


        yMomentum = yMomentum - 0.001f;
        // 强制相机位置高于0
        if (camera.position.y < 2.0f) {
            camera.position.y = 2.0f;
            yMomentum = 0;
        }
        //----------------------------------------------------------------------------------
    }

    // 释放资源
    //--------------------------------------------------------------------------------------
    CloseWindow();      // 关闭窗口和OpenGL上下文
    //--------------------------------------------------------------------------------------

    return 0;
}

优化解决方案分析

问题根源

  • 浮点数精度误差:yMomentum每次减去0.001f,累加后会产生微小精度偏差,导致camera.position.y逐渐低于2.0f。
  • UpdateCameraPro的隐式影响:调用UpdateCameraPro时,即使只传入上下移动的动量,相机的姿态计算可能因浮点数误差间接影响位置,尤其是在俯仰旋转时的向量计算。

优化方案

方案1:使用epsilon比较替代精确值判断

避免直接用< 2.0f判断,改用基于浮点数精度的epsilon阈值,防止微小误差触发不必要的重置:

const float FLOOR_HEIGHT = 2.0f;
const float EPSILON = 0.0001f;

if (camera.position.y < FLOOR_HEIGHT - EPSILON) {
    camera.position.y = FLOOR_HEIGHT;
    yMomentum = 0.0f;
}

方案2:修正yMomentum的更新逻辑

将重力更新放在UpdateCameraPro之前,确保动量计算和相机移动的顺序正确,避免帧时序导致的累积误差:

// 主循环内调整顺序
yMomentum -= 0.001f;

// 先处理碰撞/高度限制
const float FLOOR_HEIGHT = 2.0f;
if (camera.position.y < FLOOR_HEIGHT) {
    camera.position.y = FLOOR_HEIGHT;
    yMomentum = 0.0f;
}

// 再更新相机
UpdateCameraPro(&camera,
    (Vector3){0.0f, 0.0f, yMomentum},
    (Vector3){GetMouseDelta().x*0.05f, GetMouseDelta().y*0.05f, 0.0f},
    0.0f);

方案3:手动控制相机位置而非依赖UpdateCameraPro的上下移动

完全接管相机的y轴位置计算,避免UpdateCameraPro的隐式修改:

// 移除UpdateCameraPro中的y轴动量参数,改为手动更新位置
UpdateCameraPro(&camera,
    (Vector3){0.0f, 0.0f, 0.0f}, // 上下移动设为0
    (Vector3){GetMouseDelta().x*0.05f, GetMouseDelta().y*0.05f, 0.0f},
    0.0f);

// 手动更新y位置
camera.position.y += yMomentum;

// 处理地板碰撞
const float FLOOR_HEIGHT = 2.0f;
if (camera.position.y < FLOOR_HEIGHT) {
    camera.position.y = FLOOR_HEIGHT;
    yMomentum = 0.0f;
}

// 重力更新
yMomentum -= 0.001f;

方案选择

  • 方案1适合快速修复精度问题,改动最小;
  • 方案2调整逻辑顺序,解决帧时序导致的累积误差;
  • 方案3完全接管y轴控制,最彻底避免UpdateCameraPro的隐式影响,适合需要精细控制相机的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:15:24