← ziglang.inraylib-zigGitHub

3d camera fps

★★★☆by Agnis Aldiņš (@nezvers)

There is no browser build of this example yet.

Is it the same as C?

//! raylib [core] example - 3d camera fps
//!
//! Zig-API translation of examples/core/core_3d_camera_fps.c (raylib 6.2).
//! Example contributed by Agnis Aldiņš (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
//! Example licensed under an unmodified zlib/libpng license.
//! Copyright (c) 2025 Agnis Aldiņš (@nezvers)

const std = @import("std");
const rl = @import("raylib");
const rm = rl.math;

const pi: f32 = std.math.pi;

// Movement constants
const gravity: f32 = 32.0;
const max_speed: f32 = 20.0;
const crouch_speed: f32 = 5.0;
const jump_force: f32 = 12.0;
const max_accel: f32 = 150.0;
// Grounded drag
const friction: f32 = 0.86;
// Increasing air drag, increases strafing speed
const air_drag: f32 = 0.98;
// Responsiveness for turning movement direction to looked direction
const control: f32 = 15.0;
const crouch_height: f32 = 0.0;
const stand_height: f32 = 1.0;
const bottom_height: f32 = 0.5;

// Slow down diagonal movement
const normalize_input = true;

/// Body structure
const Body = struct {
    position: rl.Vector3 = .{ .x = 0, .y = 0, .z = 0 },
    velocity: rl.Vector3 = .{ .x = 0, .y = 0, .z = 0 },
    dir: rl.Vector3 = .{ .x = 0, .y = 0, .z = 0 },
    is_grounded: bool = false,
};

const sensitivity: rl.Vector2 = .{ .x = 0.001, .y = 0.001 };

var player: Body = .{};
var look_rotation: rl.Vector2 = .{ .x = 0, .y = 0 };
var head_timer: f32 = 0.0;
var walk_lerp: f32 = 0.0;
var head_lerp: f32 = stand_height;
var lean: rl.Vector2 = .{ .x = 0, .y = 0 };

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

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

    // Initialize camera variables
    // NOTE: updateCameraFps() takes care of the rest
    var camera: rl.Camera = .{
        .position = .{
            .x = player.position.x,
            .y = player.position.y + (bottom_height + head_lerp),
            .z = player.position.z,
        },
        .target = .{ .x = 0, .y = 0, .z = 0 },
        .up = .{ .x = 0, .y = 0, .z = 0 },
        .fovy = 60.0,
        .projection = .perspective,
    };

    updateCameraFps(&camera); // Update camera parameters

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

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

    // Main game loop
    while (!rl.windowShouldClose()) { // Detect window close button or ESC key
        // Update
        const mouse_delta = rl.getMouseDelta();
        look_rotation.x -= mouse_delta.x * sensitivity.x;
        look_rotation.y += mouse_delta.y * sensitivity.y;

        const sideway = axis(.d, .a);
        const forward = axis(.w, .s);
        const crouching = rl.isKeyDown(.left_control);
        updateBody(&player, look_rotation.x, sideway, forward, rl.isKeyPressed(.space), crouching);

        const delta = rl.getFrameTime();
        head_lerp = rm.lerp(head_lerp, if (crouching) crouch_height else stand_height, 20.0 * delta);
        camera.position = .{
            .x = player.position.x,
            .y = player.position.y + (bottom_height + head_lerp),
            .z = player.position.z,
        };

        if (player.is_grounded and (forward != 0 or sideway != 0)) {
            head_timer += delta * 3.0;
            walk_lerp = rm.lerp(walk_lerp, 1.0, 10.0 * delta);
            camera.fovy = rm.lerp(camera.fovy, 55.0, 5.0 * delta);
        } else {
            walk_lerp = rm.lerp(walk_lerp, 0.0, 10.0 * delta);
            camera.fovy = rm.lerp(camera.fovy, 60.0, 5.0 * delta);
        }

        lean.x = rm.lerp(lean.x, @as(f32, @floatFromInt(sideway)) * 0.02, 10.0 * delta);
        lean.y = rm.lerp(lean.y, @as(f32, @floatFromInt(forward)) * 0.015, 10.0 * delta);

        updateCameraFps(&camera);

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

        rl.clearBackground(.ray_white);

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

            drawLevel();
        }

        // Draw info box
        rl.drawRectangle(5, 5, 330, 75, rl.fade(.sky_blue, 0.5));
        rl.drawRectangleLines(5, 5, 330, 75, .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);
        const velocity_length = rm.vector2Length(.{ .x = player.velocity.x, .y = player.velocity.z });
        rl.drawText(rl.textFormat("- Velocity Len: ({d:0>6.3})", .{velocity_length}), 15, 60, 10, .black);
    }
}

/// -1, 0 or 1 from a pair of opposing keys
fn axis(positive: rl.KeyboardKey, negative: rl.KeyboardKey) i8 {
    return @as(i8, @intFromBool(rl.isKeyDown(positive))) - @intFromBool(rl.isKeyDown(negative));
}

/// Update body considering current world state
fn updateBody(body: *Body, rot: f32, side: i8, forward: i8, jump_pressed: bool, crouch_hold: bool) void {
    var input: rl.Vector2 = .{ .x = @floatFromInt(side), .y = @floatFromInt(-forward) };

    if (normalize_input and side != 0 and forward != 0) input = rm.vector2Normalize(input);

    const delta = rl.getFrameTime();

    if (!body.is_grounded) body.velocity.y -= gravity * delta;

    if (body.is_grounded and jump_pressed) {
        body.velocity.y = jump_force;
        body.is_grounded = false;

        // Sound can be played at this moment
        //rl.setSoundPitch(fx_jump, 1.0 + (rl.getRandomValue(-100, 100)*0.001));
        //rl.playSound(fx_jump);
    }

    const front: rl.Vector3 = .{ .x = @sin(rot), .y = 0.0, .z = @cos(rot) };
    const right: rl.Vector3 = .{ .x = @cos(-rot), .y = 0.0, .z = @sin(-rot) };

    const desired_dir: rl.Vector3 = .{
        .x = input.x * right.x + input.y * front.x,
        .y = 0.0,
        .z = input.x * right.z + input.y * front.z,
    };
    body.dir = rm.vector3Lerp(body.dir, desired_dir, control * delta);

    const decel = if (body.is_grounded) friction else air_drag;
    var hvel: rl.Vector3 = .{ .x = body.velocity.x * decel, .y = 0.0, .z = body.velocity.z * decel };

    const hvel_length = rm.vector3Length(hvel); // Magnitude
    if (hvel_length < max_speed * 0.01) hvel = .{ .x = 0, .y = 0, .z = 0 };

    // This is what creates strafing
    const speed = rm.vector3DotProduct(hvel, body.dir);

    // Whenever the amount of acceleration to add is clamped by the maximum acceleration constant,
    // a Player can make the speed faster by bringing the direction closer to horizontal velocity angle
    // More info here: https://youtu.be/v3zT3Z5apaM?t=165
    const top_speed = if (crouch_hold) crouch_speed else max_speed;
    const accel = rm.clamp(top_speed - speed, 0.0, max_accel * delta);
    hvel.x += body.dir.x * accel;
    hvel.z += body.dir.z * accel;

    body.velocity.x = hvel.x;
    body.velocity.z = hvel.z;

    body.position.x += body.velocity.x * delta;
    body.position.y += body.velocity.y * delta;
    body.position.z += body.velocity.z * delta;

    // Fancy collision system against the floor
    if (body.position.y <= 0.0) {
        body.position.y = 0.0;
        body.velocity.y = 0.0;
        body.is_grounded = true; // Enable jumping
    }
}

/// Update camera for FPS behaviour
fn updateCameraFps(camera: *rl.Camera) void {
    const up: rl.Vector3 = .{ .x = 0.0, .y = 1.0, .z = 0.0 };
    const target_offset: rl.Vector3 = .{ .x = 0.0, .y = 0.0, .z = -1.0 };

    // Left and right
    const yaw = rm.vector3RotateByAxisAngle(target_offset, up, look_rotation.x);

    // Clamp view up
    var max_angle_up = rm.vector3Angle(up, yaw);
    max_angle_up -= 0.001; // Avoid numerical errors
    if (-look_rotation.y > max_angle_up) look_rotation.y = -max_angle_up;

    // Clamp view down
    var max_angle_down = rm.vector3Angle(rm.vector3Negate(up), yaw);
    max_angle_down *= -1.0; // Downwards angle is negative
    max_angle_down += 0.001; // Avoid numerical errors
    if (-look_rotation.y < max_angle_down) look_rotation.y = -max_angle_down;

    // Up and down
    const right = rm.vector3Normalize(rm.vector3CrossProduct(yaw, up));

    // Rotate view vector around right axis
    var pitch_angle = -look_rotation.y - lean.y;
    pitch_angle = rm.clamp(pitch_angle, -pi / 2 + 0.0001, pi / 2 - 0.0001); // Clamp angle so it doesn't go past straight up or straight down
    const pitch = rm.vector3RotateByAxisAngle(yaw, right, pitch_angle);

    // Head animation
    // Rotate up direction around forward axis
    const head_sin = @sin(head_timer * pi);
    const head_cos = @cos(head_timer * pi);
    const step_rotation = 0.01;
    camera.up = rm.vector3RotateByAxisAngle(up, pitch, head_sin * step_rotation + lean.x);

    // Camera BOB
    const bob_side = 0.1;
    const bob_up = 0.15;
    var bobbing = rm.vector3Scale(right, head_sin * bob_side);
    bobbing.y = @abs(head_cos * bob_up);

    camera.position = rm.vector3Add(camera.position, rm.vector3Scale(bobbing, walk_lerp));
    camera.target = rm.vector3Add(camera.position, pitch);
}

/// Draw game level
fn drawLevel() void {
    const floor_extent = 25;
    const tile_size: f32 = 5.0;
    const tile_color1: rl.Color = .{ .r = 150, .g = 200, .b = 200, .a = 255 };

    // Floor tiles
    var y: i32 = -floor_extent;
    while (y < floor_extent) : (y += 1) {
        var x: i32 = -floor_extent;
        while (x < floor_extent) : (x += 1) {
            const position: rl.Vector3 = .{ .x = @as(f32, @floatFromInt(x)) * tile_size, .y = 0.0, .z = @as(f32, @floatFromInt(y)) * tile_size };
            if (y & 1 != 0 and x & 1 != 0) {
                rl.drawPlane(position, .{ .x = tile_size, .y = tile_size }, tile_color1);
            } else if (y & 1 == 0 and x & 1 == 0) {
                rl.drawPlane(position, .{ .x = tile_size, .y = tile_size }, .light_gray);
            }
        }
    }

    const tower_size: rl.Vector3 = .{ .x = 16.0, .y = 32.0, .z = 16.0 };
    const tower_color: rl.Color = .{ .r = 150, .g = 200, .b = 200, .a = 255 };

    // One tower in each quadrant
    var tower_pos: rl.Vector3 = .{ .x = 16.0, .y = 16.0, .z = 16.0 };
    for ([_]enum { none, x, z }{ .none, .x, .z, .x }) |flip| {
        switch (flip) {
            .none => {},
            .x => tower_pos.x *= -1,
            .z => tower_pos.z *= -1,
        }
        rl.drawCubeV(tower_pos, tower_size, tower_color);
        rl.drawCubeWiresV(tower_pos, tower_size, .dark_blue);
    }

    // Red sun
    rl.drawSphere(.{ .x = 300.0, .y = 300.0, .z = 0.0 }, 100.0, .{ .r = 255, .g = 0, .b = 0, .a = 255 });
}
/*******************************************************************************************
*
*   raylib [core] example - 3d camera fps
*
*   Example complexity rating: [★★★☆] 3/4
*
*   Example originally created with raylib 5.5, last time updated with raylib 5.5
*
*   Example contributed by Agnis Aldiņš (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
*
*   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) 2025 Agnis Aldiņš (@nezvers)
*
********************************************************************************************/

#include "raylib.h"

#include "raymath.h"

//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
// Movement constants
#define GRAVITY         32.0f
#define MAX_SPEED       20.0f
#define CROUCH_SPEED     5.0f
#define JUMP_FORCE      12.0f
#define MAX_ACCEL      150.0f
// Grounded drag
#define FRICTION         0.86f
// Increasing air drag, increases strafing speed
#define AIR_DRAG         0.98f
// Responsiveness for turning movement direction to looked direction
#define CONTROL         15.0f
#define CROUCH_HEIGHT    0.0f
#define STAND_HEIGHT     1.0f
#define BOTTOM_HEIGHT    0.5f

#define NORMALIZE_INPUT  0

//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// Body structure
typedef struct {
    Vector3 position;
    Vector3 velocity;
    Vector3 dir;
    bool isGrounded;
} Body;

//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static Vector2 sensitivity = { 0.001f, 0.001f };

static Body player = { 0 };
static Vector2 lookRotation = { 0 };
static float headTimer = 0.0f;
static float walkLerp = 0.0f;
static float headLerp = STAND_HEIGHT;
static Vector2 lean = { 0 };

//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
static void DrawLevel(void);
static void UpdateCameraFPS(Camera *camera);
static void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed, bool crouchHold);

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

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

    // Initialize camera variables
    // NOTE: UpdateCameraFPS() takes care of the rest
    Camera camera = { 0 };
    camera.fovy = 60.0f;
    camera.projection = CAMERA_PERSPECTIVE;
    camera.position = (Vector3){
        player.position.x,
        player.position.y + (BOTTOM_HEIGHT + headLerp),
        player.position.z,
    };

    UpdateCameraFPS(&camera); // Update camera parameters

    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
        //----------------------------------------------------------------------------------
        Vector2 mouseDelta = GetMouseDelta();
        lookRotation.x -= mouseDelta.x*sensitivity.x;
        lookRotation.y += mouseDelta.y*sensitivity.y;

        char sideway = (IsKeyDown(KEY_D) - IsKeyDown(KEY_A));
        char forward = (IsKeyDown(KEY_W) - IsKeyDown(KEY_S));
        bool crouching = IsKeyDown(KEY_LEFT_CONTROL);
        UpdateBody(&player, lookRotation.x, sideway, forward, IsKeyPressed(KEY_SPACE), crouching);

        float delta = GetFrameTime();
        headLerp = Lerp(headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0f*delta);
        camera.position = (Vector3){
            player.position.x,
            player.position.y + (BOTTOM_HEIGHT + headLerp),
            player.position.z,
        };

        if (player.isGrounded && ((forward != 0) || (sideway != 0)))
        {
            headTimer += delta*3.0f;
            walkLerp = Lerp(walkLerp, 1.0f, 10.0f*delta);
            camera.fovy = Lerp(camera.fovy, 55.0f, 5.0f*delta);
        }
        else
        {
            walkLerp = Lerp(walkLerp, 0.0f, 10.0f*delta);
            camera.fovy = Lerp(camera.fovy, 60.0f, 5.0f*delta);
        }

        lean.x = Lerp(lean.x, sideway*0.02f, 10.0f*delta);
        lean.y = Lerp(lean.y, forward*0.015f, 10.0f*delta);

        UpdateCameraFPS(&camera);
        //----------------------------------------------------------------------------------

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

            ClearBackground(RAYWHITE);

            BeginMode3D(camera);
                DrawLevel();
            EndMode3D();

            // Draw info box
            DrawRectangle(5, 5, 330, 75, Fade(SKYBLUE, 0.5f));
            DrawRectangleLines(5, 5, 330, 75, 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(TextFormat("- Velocity Len: (%06.3f)", Vector2Length((Vector2){ player.velocity.x, player.velocity.z })), 15, 60, 10, BLACK);

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

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

    return 0;
}

//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Update body considering current world state
void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed, bool crouchHold)
{
    Vector2 input = (Vector2){ (float)side, (float)-forward };

#if defined(NORMALIZE_INPUT)
    // Slow down diagonal movement
    if ((side != 0) && (forward != 0)) input = Vector2Normalize(input);
#endif

    float delta = GetFrameTime();

    if (!body->isGrounded) body->velocity.y -= GRAVITY*delta;

    if (body->isGrounded && jumpPressed)
    {
        body->velocity.y = JUMP_FORCE;
        body->isGrounded = false;

        // Sound can be played at this moment
        //SetSoundPitch(fxJump, 1.0f + (GetRandomValue(-100, 100)*0.001));
        //PlaySound(fxJump);
    }

    Vector3 front = (Vector3){ sinf(rot), 0.f, cosf(rot) };
    Vector3 right = (Vector3){ cosf(-rot), 0.f, sinf(-rot) };

    Vector3 desiredDir = (Vector3){ input.x*right.x + input.y*front.x, 0.0f, input.x*right.z + input.y*front.z, };
    body->dir = Vector3Lerp(body->dir, desiredDir, CONTROL*delta);

    float decel = (body->isGrounded ? FRICTION : AIR_DRAG);
    Vector3 hvel = (Vector3){ body->velocity.x*decel, 0.0f, body->velocity.z*decel };

    float hvelLength = Vector3Length(hvel); // Magnitude
    if (hvelLength < (MAX_SPEED*0.01f)) hvel = (Vector3){ 0 };

    // This is what creates strafing
    float speed = Vector3DotProduct(hvel, body->dir);

    // Whenever the amount of acceleration to add is clamped by the maximum acceleration constant,
    // a Player can make the speed faster by bringing the direction closer to horizontal velocity angle
    // More info here: https://youtu.be/v3zT3Z5apaM?t=165
    float maxSpeed = (crouchHold? CROUCH_SPEED : MAX_SPEED);
    float accel = Clamp(maxSpeed - speed, 0.f, MAX_ACCEL*delta);
    hvel.x += body->dir.x*accel;
    hvel.z += body->dir.z*accel;

    body->velocity.x = hvel.x;
    body->velocity.z = hvel.z;

    body->position.x += body->velocity.x*delta;
    body->position.y += body->velocity.y*delta;
    body->position.z += body->velocity.z*delta;

    // Fancy collision system against the floor
    if (body->position.y <= 0.0f)
    {
        body->position.y = 0.0f;
        body->velocity.y = 0.0f;
        body->isGrounded = true; // Enable jumping
    }
}

// Update camera for FPS behaviour
static void UpdateCameraFPS(Camera *camera)
{
    const Vector3 up = (Vector3){ 0.0f, 1.0f, 0.0f };
    const Vector3 targetOffset = (Vector3){ 0.0f, 0.0f, -1.0f };

    // Left and right
    Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, lookRotation.x);

    // Clamp view up
    float maxAngleUp = Vector3Angle(up, yaw);
    maxAngleUp -= 0.001f; // Avoid numerical errors
    if ( -(lookRotation.y) > maxAngleUp) { lookRotation.y = -maxAngleUp; }

    // Clamp view down
    float maxAngleDown = Vector3Angle(Vector3Negate(up), yaw);
    maxAngleDown *= -1.0f; // Downwards angle is negative
    maxAngleDown += 0.001f; // Avoid numerical errors
    if ( -(lookRotation.y) < maxAngleDown) { lookRotation.y = -maxAngleDown; }

    // Up and down
    Vector3 right = Vector3Normalize(Vector3CrossProduct(yaw, up));

    // Rotate view vector around right axis
    float pitchAngle = -lookRotation.y - lean.y;
    pitchAngle = Clamp(pitchAngle, -PI/2 + 0.0001f, PI/2 - 0.0001f); // Clamp angle so it doesn't go past straight up or straight down
    Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, pitchAngle);

    // Head animation
    // Rotate up direction around forward axis
    float headSin = sinf(headTimer*PI);
    float headCos = cosf(headTimer*PI);
    const float stepRotation = 0.01f;
    camera->up = Vector3RotateByAxisAngle(up, pitch, headSin*stepRotation + lean.x);

    // Camera BOB
    const float bobSide = 0.1f;
    const float bobUp = 0.15f;
    Vector3 bobbing = Vector3Scale(right, headSin*bobSide);
    bobbing.y = fabsf(headCos*bobUp);

    camera->position = Vector3Add(camera->position, Vector3Scale(bobbing, walkLerp));
    camera->target = Vector3Add(camera->position, pitch);
}

// Draw game level
static void DrawLevel(void)
{
    const int floorExtent = 25;
    const float tileSize = 5.0f;
    const Color tileColor1 = (Color){ 150, 200, 200, 255 };

    // Floor tiles
    for (int y = -floorExtent; y < floorExtent; y++)
    {
        for (int x = -floorExtent; x < floorExtent; x++)
        {
            if ((y & 1) && (x & 1))
            {
                DrawPlane((Vector3){ x*tileSize, 0.0f, y*tileSize}, (Vector2){ tileSize, tileSize }, tileColor1);
            }
            else if (!(y & 1) && !(x & 1))
            {
                DrawPlane((Vector3){ x*tileSize, 0.0f, y*tileSize}, (Vector2){ tileSize, tileSize }, LIGHTGRAY);
            }
        }
    }

    const Vector3 towerSize = (Vector3){ 16.0f, 32.0f, 16.0f };
    const Color towerColor = (Color){ 150, 200, 200, 255 };

    Vector3 towerPos = (Vector3){ 16.0f, 16.0f, 16.0f };
    DrawCubeV(towerPos, towerSize, towerColor);
    DrawCubeWiresV(towerPos, towerSize, DARKBLUE);

    towerPos.x *= -1;
    DrawCubeV(towerPos, towerSize, towerColor);
    DrawCubeWiresV(towerPos, towerSize, DARKBLUE);

    towerPos.z *= -1;
    DrawCubeV(towerPos, towerSize, towerColor);
    DrawCubeWiresV(towerPos, towerSize, DARKBLUE);

    towerPos.x *= -1;
    DrawCubeV(towerPos, towerSize, towerColor);
    DrawCubeWiresV(towerPos, towerSize, DARKBLUE);

    // Red sun
    DrawSphere((Vector3){ 300.0f, 300.0f, 0.0f }, 100.0f, (Color){ 255, 0, 0, 255 });
}

From raylib 6.2. The original on GitHub.