3d camera first person
There is no browser build of this example yet.
Is it the same as C?
- Desktop:verified The Zig version renders a final frame that is byte-identical to the C example.
- Browser:not checked yet The browser comparison does not cover this example yet.
//! 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.