0

So I was working on adding a simple "floor" (basically code that keeps the camera from going below a certain level) and jumping when I encountered a bug that made my camera slowly look down, even though my code didn't have anything to do with camera.target. Some times will dip down to a y value of maybe 1.999... if anyone has a better solution than setting a depth limit, please let me know. Here is the code that deals with preventing the camera from falling, and jumping. if anyone needs all of the code, I will be happy to provide it.

if (IsKeyDown(KEY_SPACE)) {
       if (camera.position.y <= 2.0f) {
             yMomentum = yMomentum + speed;
       }
}
// forces camera to be above 0
if (camera.position.y < 2.0f) {
    camera.position.y = 2.0f;
    yMomentum = 0;
}

edit. here is all the code if needed:

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

#define MAX_COLUMNS 20

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

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

    // Define the camera to look into our 3d world (position, target, up vector)
    Camera camera = { 0 };
    camera.position = (Vector3){ 0.0f, 2.0f, 0.0f };    // Camera position
    camera.target = (Vector3){ 1.0f, 2.0f, 4.0f };      // Camera looking at point
    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
    camera.fovy = 85.0f;                                // Camera field-of-view Y
    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
    float yMomentum = 0.0f;
    int cameraMode = CAMERA_FIRST_PERSON;


    DisableCursor();                    // Limit cursor to relative movement inside the window
    void input(float xMomentum);
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose())        // Detect window close button or ESC key
    {
     


        // Update camera computes movement internally depending on the camera mode
        // Some default standard keyboard/mouse inputs are hardcoded to simplify use
        // For advance camera controls, it's recommended to compute camera movement manually

        UpdateCameraPro(&camera,
            (Vector3) {
                0.0f,   // Move forward-backward
                0.0f,   // Move right-left
                yMomentum       // Move up-down
        },
            (Vector3) {
            GetMouseDelta().x * 0.05f,                          // Rotation: yaw
                GetMouseDelta().y * 0.05f,                          // Rotation: pitch
                0.0f                                                // Rotation: roll
        },
                0.0f);                              // Move to target (zoom)

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

        // Draw
        //----------------------------------------------------------------------------------
        BeginDrawing();

        ClearBackground(RAYWHITE);

        BeginMode3D(camera);

        DrawPlane((Vector3) { 0.0f, 0.0f, 0.0f }, (Vector2) { 32.0f, 32.0f }, LIGHTGRAY); // Draw ground
        DrawCube((Vector3) { -16.0f, 2.5f, 0.0f }, 1.0f, 5.0f, 32.0f, BLUE);    // Draw a blue wall
        DrawCube((Vector3) { 16.0f, 2.5f, 0.0f }, 1.0f, 5.0f, 32.0f, LIME);     // Draw a green wall
        DrawCube((Vector3) { 0.0f, 2.5f, 16.0f }, 32.0f, 5.0f, 1.0f, GOLD);     // Draw a yellow wall


        EndMode3D();

        // Draw info boxes



        EndDrawing();


        yMomentum = yMomentum - 0.001f;
        // forces camera to be above 0
        if (camera.position.y < 2.0f) {
            camera.position.y = 2.0f;
            yMomentum = 0;
        }
        //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    CloseWindow();      // Close window and OpenGL context
    //--------------------------------------------------------------------------------------

    return 0;
}

justtab
  • 23
  • 4
  • Could you provide more context about the 'yMomentum' variable and how it is used in your code? – Kozydot Aug 03 '23 at 02:35
  • Earlier in the program just increases momentum if I jump, and decreases it due to gravity. this function in the program sets yMomentum because you don't keep going through a floor if you jump and then land on the floor (basically it prevents the camera from clipping through the 'ground'). – justtab Aug 03 '23 at 02:46

1 Answers1

0

The first thing of note is that, unlike the example (which I assume this to be a modification of), you are not calling SetTargetFPS, so your frame rate is unbound (and extremely variable).

Adding back SetTargetFPS(60) (or even a value of say 10), and the debug code from the example

DrawText(TextFormat("- Position: (%06.3f, %06.3f, %06.3f)", camera.position.x, camera.position.y, camera.position.z), 610, 60, 10, BLACK);
DrawText(TextFormat("- Target: (%06.3f, %06.3f, %06.3f)", camera.target.x, camera.target.y, camera.target.z), 610, 75, 10, BLACK);
DrawText(TextFormat("- Up: (%06.3f, %06.3f, %06.3f)", camera.up.x, camera.up.y, camera.up.z), 610, 90, 10, BLACK);

helps to highlight the primary issue in a human-observable way.


Note that the vectors passed to UpdateCameraPro represent the amount of change that should take place (deltas). When using UpdateCameraPro, changing the Y-position of the camera changes the Y-target by the same amount.

The core issue is that when camera.position.y == 2.0f, yMomentum will be -0.001f on the next frame. On that next frame, the Y-position of the camera changes to 1.999f via UpdateCameraPro, which causes the Y-target to shift as well.

Later during that next frame, camera.position.y will be < 2.0f, which causes it to be manually reset to 2.0f (which does not shift the Y-target), and yMomentum will be reset to 0.0f.

Then on the frame after that, the cycle starts again when camera.position.y == 2.0f.

In effect, you are ping-ponging between a Y-position of 2.0f and 1.999f, and every change in this value causes the Y-target to change in a single direction, causing it to shift down.

(What is unclear to me is why this behaviour ever stops. Something to do with the way UpdateCameraPro adjusts/clamps the pitch, pitch affecting position [or vice versa], or rounding errors?)

Some basic advice would be to calculate how much change you want to occur during the frame, detecting if that change would put the camera in an invalid position, making any adjustments before calling UpdateCameraPro.

A very cursory example (press SPACE to instantly "jump"):

#include "raylib.h"
#include <stdio.h>

int main(void)
{
    InitWindow(1280, 720, "raylib [core] example - 3d camera first person");

    Camera camera = {
        .position = (Vector3) { 0.0f, 2.0f, 0.0f },
        .target = (Vector3) { 1.0f, 2.0f, 4.0f },
        .up = (Vector3) { 0.0f, 1.0f, 0.0f },
        .fovy = 85.0f,
        .projection = CAMERA_PERSPECTIVE
    };

    SetTargetFPS(60);
    DisableCursor();

    const float GROUND = 2.0f;
    const float GRAVITY = -0.05f;
    const float JUMP_HEIGHT = 1.0f;

    while (!WindowShouldClose())
    {
        float yChange = (IsKeyPressed(KEY_SPACE) && camera.position.y < (GROUND + 0.1f)) * JUMP_HEIGHT + GRAVITY;

        if (camera.position.y + yChange < GROUND)
            yChange = GROUND - camera.position.y;

        Vector2 md = GetMouseDelta();

        UpdateCameraPro(&camera,
                /* { forward-backward, left-right, up-down } */ (Vector3) { 0.0f, 0.0f, yChange },
                /* { yaw, pitch, roll } */ (Vector3) { md.x * 0.05f, md.y * 0.05f, 0.0f },
                /* zoom */ 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();

        DrawText(TextFormat("- Position: (%06.12f, %06.12f, %06.12f)", camera.position.x, camera.position.y, camera.position.z), 610, 60, 10, BLACK);
        DrawText(TextFormat("- Target: (%06.3f, %06.3f, %06.3f)", camera.target.x, camera.target.y, camera.target.z), 610, 75, 10, BLACK);
        DrawText(TextFormat("- Up: (%06.3f, %06.3f, %06.3f)", camera.up.x, camera.up.y, camera.up.z), 610, 90, 10, BLACK);
        DrawText(TextFormat("- Delta: (%06.3f, %06.3f)", md.x, md.y), 610, 105, 10, BLACK);
        DrawFPS(10, 10);

        EndDrawing();
    }

    CloseWindow();
}
Oka
  • 23,367
  • 6
  • 42
  • 53