← ziglang.inraylib-zigGitHub

3d camera first person

★★☆☆by Ramon Santamaria (@raysan5)

There is no browser build of this example yet.

Is it the same as C?

//! raylib [core] example - 3d camera first person
//!
//! Zig-API translation of examples/core/core_3d_camera_first_person.c (raylib 6.2).
//! Example licensed under an unmodified zlib/libpng license.
//! Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)

const rl = @import("raylib");

const max_columns = 20;

/// A column of the scene: random height, position and color.
const Column = struct {
    height: f32,
    position: rl.Vector3,
    color: rl.Color,
};

pub fn main() void {
    // Initialization
    const screen_width = 800;
    const screen_height = 450;

    rl.initWindow(screen_width, screen_height, "raylib [core] example - 3d camera first person");
    defer rl.closeWindow(); // Close window and OpenGL context

    // Define the camera to look into our 3d world (position, target, up vector)
    var camera: rl.Camera = .{
        .position = .{ .x = 0.0, .y = 2.0, .z = 4.0 }, // Camera position
        .target = .{ .x = 0.0, .y = 2.0, .z = 0.0 }, // Camera looking at point
        .up = .{ .x = 0.0, .y = 1.0, .z = 0.0 }, // Camera up vector (rotation towards target)
        .fovy = 60.0, // Camera field-of-view Y
        .projection = .perspective, // Camera projection type
    };

    var camera_mode: rl.CameraMode = .first_person;

    // Generates some random columns
    var columns: [max_columns]Column = undefined;
    for (&columns) |*column| {
        const height: f32 = @floatFromInt(rl.getRandomValue(1, 12));
        const x: f32 = @floatFromInt(rl.getRandomValue(-15, 15));
        const z: f32 = @floatFromInt(rl.getRandomValue(-15, 15));
        const r: u8 = @intCast(rl.getRandomValue(20, 255));
        const g: u8 = @intCast(rl.getRandomValue(10, 55));
        column.* = .{
            .height = height,
            .position = .{ .x = x, .y = height / 2.0, .z = z },
            .color = .{ .r = r, .g = g, .b = 30, .a = 255 },
        };
    }

    rl.disableCursor(); // Limit cursor to relative movement inside the window

    rl.setTargetFPS(60); // Set our game to run at 60 frames-per-second

    const up: rl.Vector3 = .{ .x = 0.0, .y = 1.0, .z = 0.0 };

    // Main game loop
    while (!rl.windowShouldClose()) { // Detect window close button or ESC key
        // Update
        // Switch camera mode (each switch also resets roll)
        const mode_keys = [_]struct { rl.KeyboardKey, rl.CameraMode }{
            .{ .one, .free },
            .{ .two, .first_person },
            .{ .three, .third_person },
            .{ .four, .orbital },
        };
        for (mode_keys) |entry| {
            const key, const mode = entry;
            if (rl.isKeyPressed(key)) {
                camera_mode = mode;
                camera.up = up; // Reset roll
            }
        }

        // Switch camera projection
        if (rl.isKeyPressed(.p)) {
            switch (camera.projection) {
                .perspective => {
                    // Create isometric view
                    camera_mode = .third_person;
                    // Note: The target distance is related to the render distance in the orthographic projection
                    camera.position = .{ .x = 0.0, .y = 2.0, .z = -100.0 };
                    camera.target = .{ .x = 0.0, .y = 2.0, .z = 0.0 };
                    camera.up = up;
                    camera.projection = .orthographic;
                    camera.fovy = 20.0; // near plane width in CAMERA_ORTHOGRAPHIC
                    rl.cameraYaw(&camera, -135 * rl.deg2rad, true);
                    rl.cameraPitch(&camera, -45 * rl.deg2rad, true, true, false);
                },
                .orthographic => {
                    // Reset to default view
                    camera_mode = .third_person;
                    camera.position = .{ .x = 0.0, .y = 2.0, .z = 10.0 };
                    camera.target = .{ .x = 0.0, .y = 2.0, .z = 0.0 };
                    camera.up = up;
                    camera.projection = .perspective;
                    camera.fovy = 60.0;
                },
            }
        }

        // Update camera computes movement internally depending on the camera mode
        // Some default standard keyboard/mouse inputs are hardcoded to simplify use
        // For advanced camera controls, it's recommended to compute camera movement manually
        rl.updateCamera(&camera, camera_mode); // Update camera

        // Draw
        rl.beginDrawing();
        defer rl.endDrawing();

        rl.clearBackground(.ray_white);

        {
            rl.beginMode3D(camera);
            defer rl.endMode3D();

            rl.drawPlane(.{ .x = 0.0, .y = 0.0, .z = 0.0 }, .{ .x = 32.0, .y = 32.0 }, .light_gray); // Draw ground
            rl.drawCube(.{ .x = -16.0, .y = 2.5, .z = 0.0 }, 1.0, 5.0, 32.0, .blue); // Draw a blue wall
            rl.drawCube(.{ .x = 16.0, .y = 2.5, .z = 0.0 }, 1.0, 5.0, 32.0, .lime); // Draw a green wall
            rl.drawCube(.{ .x = 0.0, .y = 2.5, .z = 16.0 }, 32.0, 5.0, 1.0, .gold); // Draw a yellow wall

            // Draw some cubes around
            for (columns) |column| {
                rl.drawCube(column.position, 2.0, column.height, 2.0, column.color);
                rl.drawCubeWires(column.position, 2.0, column.height, 2.0, .maroon);
            }

            // Draw player cube
            if (camera_mode == .third_person) {
                rl.drawCube(camera.target, 0.5, 0.5, 0.5, .purple);
                rl.drawCubeWires(camera.target, 0.5, 0.5, 0.5, .dark_purple);
            }
        }

        // Draw info boxes
        rl.drawRectangle(5, 5, 330, 100, rl.fade(.sky_blue, 0.5));
        rl.drawRectangleLines(5, 5, 330, 100, .blue);

        rl.drawText("Camera controls:", 15, 15, 10, .black);
        rl.drawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, .black);
        rl.drawText("- Look around: arrow keys or mouse", 15, 45, 10, .black);
        rl.drawText("- Camera mode keys: 1, 2, 3, 4", 15, 60, 10, .black);
        rl.drawText("- Zoom keys: num-plus, num-minus or mouse scroll", 15, 75, 10, .black);
        rl.drawText("- Camera projection key: P", 15, 90, 10, .black);

        rl.drawRectangle(600, 5, 195, 100, rl.fade(.sky_blue, 0.5));
        rl.drawRectangleLines(600, 5, 195, 100, .blue);

        rl.drawText("Camera status:", 610, 15, 10, .black);
        rl.drawText(rl.textFormat("- Mode: {s}", .{modeName(camera_mode)}), 610, 30, 10, .black);
        rl.drawText(rl.textFormat("- Projection: {s}", .{projectionName(camera.projection)}), 610, 45, 10, .black);
        rl.drawText(rl.textFormat("- Position: ({d:0>6.3}, {d:0>6.3}, {d:0>6.3})", .{ camera.position.x, camera.position.y, camera.position.z }), 610, 60, 10, .black);
        rl.drawText(rl.textFormat("- Target: ({d:0>6.3}, {d:0>6.3}, {d:0>6.3})", .{ camera.target.x, camera.target.y, camera.target.z }), 610, 75, 10, .black);
        rl.drawText(rl.textFormat("- Up: ({d:0>6.3}, {d:0>6.3}, {d:0>6.3})", .{ camera.up.x, camera.up.y, camera.up.z }), 610, 90, 10, .black);
    }
}

fn modeName(mode: rl.CameraMode) [:0]const u8 {
    return switch (mode) {
        .free => "FREE",
        .first_person => "FIRST_PERSON",
        .third_person => "THIRD_PERSON",
        .orbital => "ORBITAL",
        else => "CUSTOM",
    };
}

fn projectionName(projection: rl.CameraProjection) [:0]const u8 {
    return switch (projection) {
        .perspective => "PERSPECTIVE",
        .orthographic => "ORTHOGRAPHIC",
    };
}
/*******************************************************************************************
*
*   raylib [core] example - 3d camera first person
*
*   Example complexity rating: [★★☆☆] 2/4
*
*   Example originally created with raylib 1.3, last time updated with raylib 1.3
*
*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
*   BSD-like license that allows static linking with closed source software
*
*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/

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

#define MAX_COLUMNS 20

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

    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, 4.0f };    // Camera position
    camera.target = (Vector3){ 0.0f, 2.0f, 0.0f };      // Camera looking at point
    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
    camera.fovy = 60.0f;                                // Camera field-of-view Y
    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type

    int cameraMode = CAMERA_FIRST_PERSON;

    // Generates some random columns
    float heights[MAX_COLUMNS] = { 0 };
    Vector3 positions[MAX_COLUMNS] = { 0 };
    Color colors[MAX_COLUMNS] = { 0 };

    for (int i = 0; i < MAX_COLUMNS; i++)
    {
        heights[i] = (float)GetRandomValue(1, 12);
        positions[i] = (Vector3){ (float)GetRandomValue(-15, 15), heights[i]/2.0f, (float)GetRandomValue(-15, 15) };
        colors[i] = (Color){ GetRandomValue(20, 255), GetRandomValue(10, 55), 30, 255 };
    }

    DisableCursor();                    // Limit cursor to relative movement inside the window

    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose())        // Detect window close button or ESC key
    {
        // Update
        //----------------------------------------------------------------------------------
        // Switch camera mode
        if (IsKeyPressed(KEY_ONE))
        {
            cameraMode = CAMERA_FREE;
            camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll
        }

        if (IsKeyPressed(KEY_TWO))
        {
            cameraMode = CAMERA_FIRST_PERSON;
            camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll
        }

        if (IsKeyPressed(KEY_THREE))
        {
            cameraMode = CAMERA_THIRD_PERSON;
            camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll
        }

        if (IsKeyPressed(KEY_FOUR))
        {
            cameraMode = CAMERA_ORBITAL;
            camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll
        }

        // Switch camera projection
        if (IsKeyPressed(KEY_P))
        {
            if (camera.projection == CAMERA_PERSPECTIVE)
            {
                // Create isometric view
                cameraMode = CAMERA_THIRD_PERSON;
                // Note: The target distance is related to the render distance in the orthographic projection
                camera.position = (Vector3){ 0.0f, 2.0f, -100.0f };
                camera.target = (Vector3){ 0.0f, 2.0f, 0.0f };
                camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
                camera.projection = CAMERA_ORTHOGRAPHIC;
                camera.fovy = 20.0f; // near plane width in CAMERA_ORTHOGRAPHIC
                CameraYaw(&camera, -135*DEG2RAD, true);
                CameraPitch(&camera, -45*DEG2RAD, true, true, false);
            }
            else if (camera.projection == CAMERA_ORTHOGRAPHIC)
            {
                // Reset to default view
                cameraMode = CAMERA_THIRD_PERSON;
                camera.position = (Vector3){ 0.0f, 2.0f, 10.0f };
                camera.target = (Vector3){ 0.0f, 2.0f, 0.0f };
                camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
                camera.projection = CAMERA_PERSPECTIVE;
                camera.fovy = 60.0f;
            }
        }

        // Update camera computes movement internally depending on the camera mode
        // Some default standard keyboard/mouse inputs are hardcoded to simplify use
        // For advanced camera controls, it's recommended to compute camera movement manually
        UpdateCamera(&camera, cameraMode);                  // Update camera
/*
        // Camera PRO usage example (EXPERIMENTAL)
        // This new camera function allows custom movement/rotation values to be directly provided
        // as input parameters, with this approach, rcamera module is internally independent of raylib inputs
        UpdateCameraPro(&camera,
            (Vector3){
                (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f -      // Move forward-backward
                (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f,
                (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f -   // Move right-left
                (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f,
                0.0f                                                // Move up-down
            },
            (Vector3){
                GetMouseDelta().x*0.05f,                            // Rotation: yaw
                GetMouseDelta().y*0.05f,                            // Rotation: pitch
                0.0f                                                // Rotation: roll
            },
            GetMouseWheelMove()*2.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

                // Draw some cubes around
                for (int i = 0; i < MAX_COLUMNS; i++)
                {
                    DrawCube(positions[i], 2.0f, heights[i], 2.0f, colors[i]);
                    DrawCubeWires(positions[i], 2.0f, heights[i], 2.0f, MAROON);
                }

                // Draw player cube
                if (cameraMode == CAMERA_THIRD_PERSON)
                {
                    DrawCube(camera.target, 0.5f, 0.5f, 0.5f, PURPLE);
                    DrawCubeWires(camera.target, 0.5f, 0.5f, 0.5f, DARKPURPLE);
                }

            EndMode3D();

            // Draw info boxes
            DrawRectangle(5, 5, 330, 100, Fade(SKYBLUE, 0.5f));
            DrawRectangleLines(5, 5, 330, 100, BLUE);

            DrawText("Camera controls:", 15, 15, 10, BLACK);
            DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, BLACK);
            DrawText("- Look around: arrow keys or mouse", 15, 45, 10, BLACK);
            DrawText("- Camera mode keys: 1, 2, 3, 4", 15, 60, 10, BLACK);
            DrawText("- Zoom keys: num-plus, num-minus or mouse scroll", 15, 75, 10, BLACK);
            DrawText("- Camera projection key: P", 15, 90, 10, BLACK);

            DrawRectangle(600, 5, 195, 100, Fade(SKYBLUE, 0.5f));
            DrawRectangleLines(600, 5, 195, 100, BLUE);

            DrawText("Camera status:", 610, 15, 10, BLACK);
            DrawText(TextFormat("- Mode: %s", (cameraMode == CAMERA_FREE) ? "FREE" :
                                              (cameraMode == CAMERA_FIRST_PERSON) ? "FIRST_PERSON" :
                                              (cameraMode == CAMERA_THIRD_PERSON) ? "THIRD_PERSON" :
                                              (cameraMode == CAMERA_ORBITAL) ? "ORBITAL" : "CUSTOM"), 610, 30, 10, BLACK);
            DrawText(TextFormat("- Projection: %s", (camera.projection == CAMERA_PERSPECTIVE) ? "PERSPECTIVE" :
                                                    (camera.projection == CAMERA_ORTHOGRAPHIC) ? "ORTHOGRAPHIC" : "CUSTOM"), 610, 45, 10, BLACK);
            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);

        EndDrawing();
        //----------------------------------------------------------------------------------
    }

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

    return 0;
}

From raylib 6.2. The original on GitHub.