From 75150343df939d8db5343a65f41abaf5f8447ecd Mon Sep 17 00:00:00 2001 From: GCodergr Date: Sun, 7 Sep 2025 16:58:57 +0300 Subject: [PATCH 1/5] Added core_3d_camera_fps.odin example --- .github/workflows/check.yml | 1 + raylib/ports/core/core_3d_camera_fps.odin | 319 ++++++++++++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 raylib/ports/core/core_3d_camera_fps.odin diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e64e2f8..955fb4f 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -76,6 +76,7 @@ jobs: odin check raylib/ports/textures/textures_gif_player.odin -file $FLAGS odin check raylib/tetroid $FLAGS odin check raylib/box2d $FLAGS + odin check raylib/ports/core/core_3d_camera_fps.odin -file $FLAGS odin check directx/d3d11_minimal_sdl2 -target:windows_amd64 $FLAGS diff --git a/raylib/ports/core/core_3d_camera_fps.odin b/raylib/ports/core/core_3d_camera_fps.odin new file mode 100644 index 0000000..6b3763c --- /dev/null +++ b/raylib/ports/core/core_3d_camera_fps.odin @@ -0,0 +1,319 @@ +/******************************************************************************************* +* +* 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 Aldins (@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 Aldins (@nezvers) +* +********************************************************************************************/ + +package main + +import "core:math/linalg" +import rl "vendor:raylib" + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Movement constants +GRAVITY :: 32.0 +MAX_SPEED :: 20.0 +CROUCH_SPEED :: 5.0 +JUMP_FORCE :: 12.0 +MAX_ACCEL :: 150.0 +// Grounded drag +FRICTION :: 0.86 +// Increasing air drag, increases strafing speed +AIR_DRAG :: 0.98 +// Responsiveness for turning movement direction to looked direction +CONTROL :: 15.0 +CROUCH_HEIGHT :: 0.0 +STAND_HEIGHT :: 1.0 +BOTTOM_HEIGHT :: 0.5 + +NORMALIZE_INPUT :: false + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Body structure +Body :: struct { + position: rl.Vector3, + velocity: rl.Vector3, + dir: rl.Vector3, + isGrounded: bool, +} + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +sensitivity: rl.Vector2 = {0.001, 0.001} + +player: Body +lookRotation: rl.Vector2 = {0.0, 0.0} +headTimer: f32 = 0.0 +walkLerp: f32 = 0.0 +headLerp: f32 = STAND_HEIGHT +lean: rl.Vector2 = {0.0, 0.0} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +main :: proc() { + // Initialization + //-------------------------------------------------------------------------------------- + SCREEN_WIDTH :: 800 + SCREEN_HEIGHT :: 450 + + rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - 3d camera fps") + + // Initialize camera variables + // NOTE: UpdateCameraFPS() takes care of the rest + camera: rl.Camera + camera.fovy = 60.0 + camera.projection = .PERSPECTIVE + camera.position = rl.Vector3 { + player.position.x, + player.position.y + (BOTTOM_HEIGHT + headLerp), + player.position.z, + } + + update_camera_fps(&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 + for !rl.WindowShouldClose() { // Detect window close button or ESC key + // Update + //---------------------------------------------------------------------------------- + mouseDelta: rl.Vector2 = rl.GetMouseDelta() + lookRotation.x -= mouseDelta.x * sensitivity.x + lookRotation.y += mouseDelta.y * sensitivity.y + + sideway := i8(i8(rl.IsKeyDown(.D)) - i8(rl.IsKeyDown(.A))) + forward := i8(i8(rl.IsKeyDown(.W)) - i8(rl.IsKeyDown(.S))) + crouching := rl.IsKeyDown(.LEFT_CONTROL) + update_body(&player, lookRotation.x, sideway, forward, rl.IsKeyPressed(.SPACE), crouching) + + delta: f32 = rl.GetFrameTime() + headLerp = rl.Lerp(headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0 * delta) + camera.position = rl.Vector3 { + player.position.x, + player.position.y + (BOTTOM_HEIGHT + headLerp), + player.position.z, + } + + if player.isGrounded && ((forward != 0) || (sideway != 0)) { + headTimer += delta * 3.0 + walkLerp = rl.Lerp(walkLerp, 1.0, 10.0 * delta) + camera.fovy = rl.Lerp(camera.fovy, 55.0, 5.0 * delta) + } else { + walkLerp = rl.Lerp(walkLerp, 0.0, 10.0 * delta) + camera.fovy = rl.Lerp(camera.fovy, 60.0, 5.0 * delta) + } + + lean.x = rl.Lerp(lean.x, f32(sideway) * 0.02, 10.0 * delta) + lean.y = rl.Lerp(lean.y, f32(forward) * 0.015, 10.0 * delta) + + update_camera_fps(&camera) + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + rl.BeginDrawing() + + rl.ClearBackground(rl.RAYWHITE) + + rl.BeginMode3D(camera) + draw_level() + rl.EndMode3D() + + // Draw info box + rl.DrawRectangle(5, 5, 330, 75, rl.Fade(rl.SKYBLUE, 0.5)) + rl.DrawRectangleLines(5, 5, 330, 75, rl.BLUE) + + rl.DrawText("Camera controls:", 15, 15, 10, rl.BLACK) + rl.DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, rl.BLACK) + rl.DrawText("- Look around: arrow keys or mouse", 15, 45, 10, rl.BLACK) + rl.DrawText(rl.TextFormat("- Velocity Len: (%06.3f)", rl.Vector2Length(rl.Vector2{player.velocity.x, player.velocity.z})), 15, 60, 10, rl.BLACK) + + rl.EndDrawing() + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + rl.CloseWindow() // Close window and OpenGL context + //-------------------------------------------------------------------------------------- +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +// Update body considering current world state +update_body :: proc(body: ^Body,rot: f32, side: i8, forward: i8, jumpPressed: bool, crouchHold: bool) { + input: rl.Vector2 = {f32(side), f32(-forward)} + + if NORMALIZE_INPUT { + // Slow down diagonal movement + if (side != 0) && (forward != 0) { + input = rl.Vector2Normalize(input) + } + } + + delta: f32 = rl.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); + } + + front: rl.Vector3 = rl.Vector3{linalg.sin(rot), 0, linalg.cos(rot)} + right: rl.Vector3 = rl.Vector3{linalg.cos(-rot), 0, linalg.sin(-rot)} + + desiredDir: rl.Vector3 = rl.Vector3 { + input.x * right.x + input.y * front.x, + 0.0, + input.x * right.z + input.y * front.z, + } + body.dir = linalg.lerp(body.dir, desiredDir, CONTROL * delta) + + decel: f32 = (body.isGrounded ? FRICTION : AIR_DRAG) + hvel: rl.Vector3 = rl.Vector3{body.velocity.x * decel, 0.0, body.velocity.z * decel} + + hvelLength: f32 = rl.Vector3Length(hvel) // Magnitude + if hvelLength < (MAX_SPEED * 0.01) { + hvel = rl.Vector3{0.0, 0.0, 0.0} + } + + // TODO(DimitrisE): Check the result + // This is what creates strafing + speed := rl.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 + maxSpeed: f32 = (crouchHold ? CROUCH_SPEED : MAX_SPEED) + accel: f32 = rl.Clamp(maxSpeed - speed, 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.isGrounded = true // Enable jumping + } +} + +// Update camera for FPS behaviour +update_camera_fps :: proc(camera: ^rl.Camera) { + up: rl.Vector3 : rl.Vector3{0.0, 1.0, 0.0} + targetOffset: rl.Vector3 : rl.Vector3{0.0, 0.0, -1.0} + + // Left and right + yaw: rl.Vector3 = rl.Vector3RotateByAxisAngle(targetOffset, up, lookRotation.x) + + // Clamp view up + maxAngleUp: f32 = rl.Vector3Angle(up, yaw) + maxAngleUp -= 0.001 // Avoid numerical errors + if (-(lookRotation.y) > maxAngleUp) {lookRotation.y = -maxAngleUp} + + // Clamp view down + maxAngleDown: f32 = rl.Vector3Angle(-up, yaw) + maxAngleDown *= -1.0 // Downwards angle is negative + maxAngleDown += 0.001 // Avoid numerical errors + if -(lookRotation.y) < maxAngleDown { + lookRotation.y = -maxAngleDown + } + + // Up and down + right: rl.Vector3 = rl.Vector3Normalize(rl.Vector3CrossProduct(yaw, up)) + + // Rotate view vector around right axis + pitchAngle: f32 = -lookRotation.y - lean.y + pitchAngle = rl.Clamp(pitchAngle, -rl.PI / 2 + 0.0001, rl.PI / 2 - 0.0001) // Clamp angle so it doesn't go past straight up or straight down + pitch: rl.Vector3 = rl.Vector3RotateByAxisAngle(yaw, right, pitchAngle) + + // Head animation + // Rotate up direction around forward axis + headSin: f32 = linalg.sin(headTimer * rl.PI) + headCos: f32 = linalg.cos(headTimer * rl.PI) + STEP_ROTATION: f32 : 0.01 + camera.up = rl.Vector3RotateByAxisAngle(up, pitch, headSin * STEP_ROTATION + lean.x) + + // Camera BOB + BOB_SIDE: f32 : 0.1 + BOB_UP: f32 : 0.15 + bobbing: rl.Vector3 = right * (headSin * BOB_SIDE) + bobbing.y = abs(headCos * BOB_UP) + + camera.position = camera.position + (bobbing * walkLerp) + camera.target = camera.position + pitch +} + +// Draw game level +draw_level :: proc() { + FLOOR_EXTENT: int : 25 + TILE_SIZE: f32 : 5.0 + TILE_COLOR_1 := rl.Color{150, 200, 200, 255} + + // Floor tiles + for y: int = -FLOOR_EXTENT; y < FLOOR_EXTENT; y += 1 { + for x: int = -FLOOR_EXTENT; x < FLOOR_EXTENT; x += 1 { + if (y & 1 != 0) && (x & 1 != 0) { + rl.DrawPlane(rl.Vector3{f32(x) * TILE_SIZE, 0.0, f32(y) * TILE_SIZE}, rl.Vector2{TILE_SIZE, TILE_SIZE}, TILE_COLOR_1) + } else if (y & 1 == 0) && (x & 1 == 0) { + rl.DrawPlane(rl.Vector3{f32(x) * TILE_SIZE, 0.0, f32(y) * TILE_SIZE}, rl.Vector2{TILE_SIZE, TILE_SIZE}, rl.LIGHTGRAY) + } + } + } + + TOWER_SIZE: rl.Vector3 : rl.Vector3{16.0, 32.0, 16.0} + TOWER_COLOR: rl.Color : rl.Color{150, 200, 200, 255} + + towerPos: rl.Vector3 = rl.Vector3{16.0, 16.0, 16.0} + rl.DrawCubeV(towerPos, TOWER_SIZE, TOWER_COLOR) + rl.DrawCubeWiresV(towerPos, TOWER_SIZE, rl.DARKBLUE) + + towerPos.x *= -1 + rl.DrawCubeV(towerPos, TOWER_SIZE, TOWER_COLOR) + rl.DrawCubeWiresV(towerPos, TOWER_SIZE, rl.DARKBLUE) + + towerPos.z *= -1 + rl.DrawCubeV(towerPos, TOWER_SIZE, TOWER_COLOR) + rl.DrawCubeWiresV(towerPos, TOWER_SIZE, rl.DARKBLUE) + + towerPos.x *= -1 + rl.DrawCubeV(towerPos, TOWER_SIZE, TOWER_COLOR) + rl.DrawCubeWiresV(towerPos, TOWER_SIZE, rl.DARKBLUE) + + // Red sun + rl.DrawSphere(rl.Vector3{300.0, 300.0, 0.0}, 100, rl.Color{255, 0, 0, 255}) +} From 105f580f137ff2d646bbcd517dfe69332d4293de Mon Sep 17 00:00:00 2001 From: GCodergr Date: Sun, 7 Sep 2025 17:36:25 +0300 Subject: [PATCH 2/5] Removed deprecated comment --- raylib/ports/core/core_3d_camera_fps.odin | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/raylib/ports/core/core_3d_camera_fps.odin b/raylib/ports/core/core_3d_camera_fps.odin index 6b3763c..1832eb7 100644 --- a/raylib/ports/core/core_3d_camera_fps.odin +++ b/raylib/ports/core/core_3d_camera_fps.odin @@ -204,8 +204,7 @@ update_body :: proc(body: ^Body,rot: f32, side: i8, forward: i8, jumpPressed: bo if hvelLength < (MAX_SPEED * 0.01) { hvel = rl.Vector3{0.0, 0.0, 0.0} } - - // TODO(DimitrisE): Check the result + // This is what creates strafing speed := rl.Vector3DotProduct(hvel, body.dir) From 6abe07edf47e4021921a5f9530c18774df2a797e Mon Sep 17 00:00:00 2001 From: GCodergr Date: Sat, 4 Oct 2025 18:48:06 +0300 Subject: [PATCH 3/5] Refactored code based on feedback, naming and style conventions --- raylib/ports/core/core_3d_camera_fps.odin | 82 +++++++++++------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/raylib/ports/core/core_3d_camera_fps.odin b/raylib/ports/core/core_3d_camera_fps.odin index 1832eb7..6dce0d1 100644 --- a/raylib/ports/core/core_3d_camera_fps.odin +++ b/raylib/ports/core/core_3d_camera_fps.odin @@ -55,14 +55,14 @@ Body :: struct { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -sensitivity: rl.Vector2 = {0.001, 0.001} +sensitivity := rl.Vector2{0.001, 0.001} player: Body -lookRotation: rl.Vector2 = {0.0, 0.0} +lookRotation := rl.Vector2{0.0, 0.0} headTimer: f32 = 0.0 walkLerp: f32 = 0.0 headLerp: f32 = STAND_HEIGHT -lean: rl.Vector2 = {0.0, 0.0} +lean := rl.Vector2{0.0, 0.0} //------------------------------------------------------------------------------------ // Program main entry point @@ -77,13 +77,13 @@ main :: proc() { // Initialize camera variables // NOTE: UpdateCameraFPS() takes care of the rest - camera: rl.Camera - camera.fovy = 60.0 - camera.projection = .PERSPECTIVE - camera.position = rl.Vector3 { + camera := rl.Camera { + fovy = 60.0, + projection = .PERSPECTIVE, + position = rl.Vector3 { player.position.x, player.position.y + (BOTTOM_HEIGHT + headLerp), - player.position.z, + player.position.z }, } update_camera_fps(&camera) // Update camera parameters @@ -97,16 +97,16 @@ main :: proc() { for !rl.WindowShouldClose() { // Detect window close button or ESC key // Update //---------------------------------------------------------------------------------- - mouseDelta: rl.Vector2 = rl.GetMouseDelta() + mouseDelta := rl.GetMouseDelta() lookRotation.x -= mouseDelta.x * sensitivity.x lookRotation.y += mouseDelta.y * sensitivity.y - sideway := i8(i8(rl.IsKeyDown(.D)) - i8(rl.IsKeyDown(.A))) - forward := i8(i8(rl.IsKeyDown(.W)) - i8(rl.IsKeyDown(.S))) + sideway := i8(rl.IsKeyDown(.D)) - i8(rl.IsKeyDown(.A)) + forward := i8(rl.IsKeyDown(.W)) - i8(rl.IsKeyDown(.S)) crouching := rl.IsKeyDown(.LEFT_CONTROL) update_body(&player, lookRotation.x, sideway, forward, rl.IsKeyPressed(.SPACE), crouching) - delta: f32 = rl.GetFrameTime() + delta := rl.GetFrameTime() headLerp = rl.Lerp(headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0 * delta) camera.position = rl.Vector3 { player.position.x, @@ -172,13 +172,13 @@ update_body :: proc(body: ^Body,rot: f32, side: i8, forward: i8, jumpPressed: bo } } - delta: f32 = rl.GetFrameTime() + delta := rl.GetFrameTime() if !body.isGrounded { body.velocity.y -= GRAVITY * delta } - if (body.isGrounded && jumpPressed) { + if body.isGrounded && jumpPressed { body.velocity.y = JUMP_FORCE body.isGrounded = false @@ -187,20 +187,20 @@ update_body :: proc(body: ^Body,rot: f32, side: i8, forward: i8, jumpPressed: bo //PlaySound(fxJump); } - front: rl.Vector3 = rl.Vector3{linalg.sin(rot), 0, linalg.cos(rot)} - right: rl.Vector3 = rl.Vector3{linalg.cos(-rot), 0, linalg.sin(-rot)} + front := rl.Vector3{linalg.sin(rot), 0, linalg.cos(rot)} + right := rl.Vector3{linalg.cos(-rot), 0, linalg.sin(-rot)} - desiredDir: rl.Vector3 = rl.Vector3 { + desiredDir := rl.Vector3 { input.x * right.x + input.y * front.x, 0.0, input.x * right.z + input.y * front.z, } body.dir = linalg.lerp(body.dir, desiredDir, CONTROL * delta) - decel: f32 = (body.isGrounded ? FRICTION : AIR_DRAG) - hvel: rl.Vector3 = rl.Vector3{body.velocity.x * decel, 0.0, body.velocity.z * decel} + decel : f32 = (body.isGrounded ? FRICTION : AIR_DRAG) + hvel := rl.Vector3{body.velocity.x * decel, 0.0, body.velocity.z * decel} - hvelLength: f32 = rl.Vector3Length(hvel) // Magnitude + hvelLength := rl.Vector3Length(hvel) // Magnitude if hvelLength < (MAX_SPEED * 0.01) { hvel = rl.Vector3{0.0, 0.0, 0.0} } @@ -212,7 +212,7 @@ update_body :: proc(body: ^Body,rot: f32, side: i8, forward: i8, jumpPressed: bo // 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 maxSpeed: f32 = (crouchHold ? CROUCH_SPEED : MAX_SPEED) - accel: f32 = rl.Clamp(maxSpeed - speed, 0, MAX_ACCEL * delta) + accel := rl.Clamp(maxSpeed - speed, 0, MAX_ACCEL * delta) hvel.x += body.dir.x * accel hvel.z += body.dir.z * accel @@ -233,19 +233,19 @@ update_body :: proc(body: ^Body,rot: f32, side: i8, forward: i8, jumpPressed: bo // Update camera for FPS behaviour update_camera_fps :: proc(camera: ^rl.Camera) { - up: rl.Vector3 : rl.Vector3{0.0, 1.0, 0.0} - targetOffset: rl.Vector3 : rl.Vector3{0.0, 0.0, -1.0} + up :: rl.Vector3{0.0, 1.0, 0.0} + targetOffset :: rl.Vector3{0.0, 0.0, -1.0} // Left and right - yaw: rl.Vector3 = rl.Vector3RotateByAxisAngle(targetOffset, up, lookRotation.x) + yaw := rl.Vector3RotateByAxisAngle(targetOffset, up, lookRotation.x) // Clamp view up - maxAngleUp: f32 = rl.Vector3Angle(up, yaw) + maxAngleUp := rl.Vector3Angle(up, yaw) maxAngleUp -= 0.001 // Avoid numerical errors if (-(lookRotation.y) > maxAngleUp) {lookRotation.y = -maxAngleUp} // Clamp view down - maxAngleDown: f32 = rl.Vector3Angle(-up, yaw) + maxAngleDown := rl.Vector3Angle(-up, yaw) maxAngleDown *= -1.0 // Downwards angle is negative maxAngleDown += 0.001 // Avoid numerical errors if -(lookRotation.y) < maxAngleDown { @@ -253,24 +253,24 @@ update_camera_fps :: proc(camera: ^rl.Camera) { } // Up and down - right: rl.Vector3 = rl.Vector3Normalize(rl.Vector3CrossProduct(yaw, up)) + right := rl.Vector3Normalize(rl.Vector3CrossProduct(yaw, up)) // Rotate view vector around right axis - pitchAngle: f32 = -lookRotation.y - lean.y + pitchAngle := -lookRotation.y - lean.y pitchAngle = rl.Clamp(pitchAngle, -rl.PI / 2 + 0.0001, rl.PI / 2 - 0.0001) // Clamp angle so it doesn't go past straight up or straight down - pitch: rl.Vector3 = rl.Vector3RotateByAxisAngle(yaw, right, pitchAngle) + pitch := rl.Vector3RotateByAxisAngle(yaw, right, pitchAngle) // Head animation // Rotate up direction around forward axis - headSin: f32 = linalg.sin(headTimer * rl.PI) - headCos: f32 = linalg.cos(headTimer * rl.PI) - STEP_ROTATION: f32 : 0.01 + headSin := linalg.sin(headTimer * rl.PI) + headCos := linalg.cos(headTimer * rl.PI) + STEP_ROTATION :: 0.01 camera.up = rl.Vector3RotateByAxisAngle(up, pitch, headSin * STEP_ROTATION + lean.x) // Camera BOB - BOB_SIDE: f32 : 0.1 - BOB_UP: f32 : 0.15 - bobbing: rl.Vector3 = right * (headSin * BOB_SIDE) + BOB_SIDE :: 0.1 + BOB_UP :: 0.15 + bobbing := right * (headSin * BOB_SIDE) bobbing.y = abs(headCos * BOB_UP) camera.position = camera.position + (bobbing * walkLerp) @@ -279,8 +279,8 @@ update_camera_fps :: proc(camera: ^rl.Camera) { // Draw game level draw_level :: proc() { - FLOOR_EXTENT: int : 25 - TILE_SIZE: f32 : 5.0 + FLOOR_EXTENT :: 25 + TILE_SIZE :: 5.0 TILE_COLOR_1 := rl.Color{150, 200, 200, 255} // Floor tiles @@ -294,10 +294,10 @@ draw_level :: proc() { } } - TOWER_SIZE: rl.Vector3 : rl.Vector3{16.0, 32.0, 16.0} - TOWER_COLOR: rl.Color : rl.Color{150, 200, 200, 255} + TOWER_SIZE :: rl.Vector3{16.0, 32.0, 16.0} + TOWER_COLOR :: rl.Color{150, 200, 200, 255} - towerPos: rl.Vector3 = rl.Vector3{16.0, 16.0, 16.0} + towerPos := rl.Vector3{16.0, 16.0, 16.0} rl.DrawCubeV(towerPos, TOWER_SIZE, TOWER_COLOR) rl.DrawCubeWiresV(towerPos, TOWER_SIZE, rl.DARKBLUE) @@ -315,4 +315,4 @@ draw_level :: proc() { // Red sun rl.DrawSphere(rl.Vector3{300.0, 300.0, 0.0}, 100, rl.Color{255, 0, 0, 255}) -} +} \ No newline at end of file From eca920497c3ed43478a246cace7ef4f86c67c228 Mon Sep 17 00:00:00 2001 From: GCodergr Date: Wed, 8 Oct 2025 23:53:37 +0300 Subject: [PATCH 4/5] Updated code to follow naming, style conventions and odin features --- raylib/ports/core/core_3d_camera_fps.odin | 57 ++++++++++------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/raylib/ports/core/core_3d_camera_fps.odin b/raylib/ports/core/core_3d_camera_fps.odin index 6dce0d1..9e68063 100644 --- a/raylib/ports/core/core_3d_camera_fps.odin +++ b/raylib/ports/core/core_3d_camera_fps.odin @@ -15,7 +15,7 @@ * ********************************************************************************************/ -package main +package raylib_examples import "core:math/linalg" import rl "vendor:raylib" @@ -58,11 +58,11 @@ Body :: struct { sensitivity := rl.Vector2{0.001, 0.001} player: Body -lookRotation := rl.Vector2{0.0, 0.0} -headTimer: f32 = 0.0 -walkLerp: f32 = 0.0 +lookRotation: rl.Vector2 +headTimer: f32 +walkLerp: f32 headLerp: f32 = STAND_HEIGHT -lean := rl.Vector2{0.0, 0.0} +lean: rl.Vector2 //------------------------------------------------------------------------------------ // Program main entry point @@ -80,10 +80,7 @@ main :: proc() { camera := rl.Camera { fovy = 60.0, projection = .PERSPECTIVE, - position = rl.Vector3 { - player.position.x, - player.position.y + (BOTTOM_HEIGHT + headLerp), - player.position.z }, + position = { player.position.x, player.position.y + (BOTTOM_HEIGHT + headLerp), player.position.z }, } update_camera_fps(&camera) // Update camera parameters @@ -108,11 +105,7 @@ main :: proc() { delta := rl.GetFrameTime() headLerp = rl.Lerp(headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0 * delta) - camera.position = rl.Vector3 { - player.position.x, - player.position.y + (BOTTOM_HEIGHT + headLerp), - player.position.z, - } + camera.position = { player.position.x, player.position.y + (BOTTOM_HEIGHT + headLerp), player.position.z } if player.isGrounded && ((forward != 0) || (sideway != 0)) { headTimer += delta * 3.0 @@ -146,7 +139,7 @@ main :: proc() { rl.DrawText("Camera controls:", 15, 15, 10, rl.BLACK) rl.DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, rl.BLACK) rl.DrawText("- Look around: arrow keys or mouse", 15, 45, 10, rl.BLACK) - rl.DrawText(rl.TextFormat("- Velocity Len: (%06.3f)", rl.Vector2Length(rl.Vector2{player.velocity.x, player.velocity.z})), 15, 60, 10, rl.BLACK) + rl.DrawText(rl.TextFormat("- Velocity Len: (%06.3f)", rl.Vector2Length(player.velocity.xy)), 15, 60, 10, rl.BLACK) rl.EndDrawing() //---------------------------------------------------------------------------------- @@ -162,7 +155,7 @@ main :: proc() { // Module Functions Definition //---------------------------------------------------------------------------------- // Update body considering current world state -update_body :: proc(body: ^Body,rot: f32, side: i8, forward: i8, jumpPressed: bool, crouchHold: bool) { +update_body :: proc(body: ^Body, rot: f32, side: i8, forward: i8, jumpPressed: bool, crouchHold: bool) { input: rl.Vector2 = {f32(side), f32(-forward)} if NORMALIZE_INPUT { @@ -219,9 +212,7 @@ update_body :: proc(body: ^Body,rot: f32, side: i8, forward: i8, jumpPressed: bo 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 + body.position += body.velocity * delta // Fancy collision system against the floor if body.position.y <= 0.0 { @@ -233,27 +224,29 @@ update_body :: proc(body: ^Body,rot: f32, side: i8, forward: i8, jumpPressed: bo // Update camera for FPS behaviour update_camera_fps :: proc(camera: ^rl.Camera) { - up :: rl.Vector3{0.0, 1.0, 0.0} - targetOffset :: rl.Vector3{0.0, 0.0, -1.0} + UP :: rl.Vector3{0.0, 1.0, 0.0} + TARGET_OFFSET :: rl.Vector3{0.0, 0.0, -1.0} // Left and right - yaw := rl.Vector3RotateByAxisAngle(targetOffset, up, lookRotation.x) + yaw := rl.Vector3RotateByAxisAngle(TARGET_OFFSET, UP, lookRotation.x) // Clamp view up - maxAngleUp := rl.Vector3Angle(up, yaw) + maxAngleUp := rl.Vector3Angle(UP, yaw) maxAngleUp -= 0.001 // Avoid numerical errors - if (-(lookRotation.y) > maxAngleUp) {lookRotation.y = -maxAngleUp} + if -lookRotation.y > maxAngleUp { + lookRotation.y = -maxAngleUp + } // Clamp view down - maxAngleDown := rl.Vector3Angle(-up, yaw) + maxAngleDown := rl.Vector3Angle(-UP, yaw) maxAngleDown *= -1.0 // Downwards angle is negative maxAngleDown += 0.001 // Avoid numerical errors - if -(lookRotation.y) < maxAngleDown { + if -lookRotation.y < maxAngleDown { lookRotation.y = -maxAngleDown } // Up and down - right := rl.Vector3Normalize(rl.Vector3CrossProduct(yaw, up)) + right := rl.Vector3Normalize(rl.Vector3CrossProduct(yaw, UP)) // Rotate view vector around right axis pitchAngle := -lookRotation.y - lean.y @@ -265,7 +258,7 @@ update_camera_fps :: proc(camera: ^rl.Camera) { headSin := linalg.sin(headTimer * rl.PI) headCos := linalg.cos(headTimer * rl.PI) STEP_ROTATION :: 0.01 - camera.up = rl.Vector3RotateByAxisAngle(up, pitch, headSin * STEP_ROTATION + lean.x) + camera.up = rl.Vector3RotateByAxisAngle(UP, pitch, headSin * STEP_ROTATION + lean.x) // Camera BOB BOB_SIDE :: 0.1 @@ -281,11 +274,11 @@ update_camera_fps :: proc(camera: ^rl.Camera) { draw_level :: proc() { FLOOR_EXTENT :: 25 TILE_SIZE :: 5.0 - TILE_COLOR_1 := rl.Color{150, 200, 200, 255} + TILE_COLOR_1 :: rl.Color{150, 200, 200, 255} // Floor tiles - for y: int = -FLOOR_EXTENT; y < FLOOR_EXTENT; y += 1 { - for x: int = -FLOOR_EXTENT; x < FLOOR_EXTENT; x += 1 { + for y in -FLOOR_EXTENT..< FLOOR_EXTENT { + for x in -FLOOR_EXTENT..< FLOOR_EXTENT { if (y & 1 != 0) && (x & 1 != 0) { rl.DrawPlane(rl.Vector3{f32(x) * TILE_SIZE, 0.0, f32(y) * TILE_SIZE}, rl.Vector2{TILE_SIZE, TILE_SIZE}, TILE_COLOR_1) } else if (y & 1 == 0) && (x & 1 == 0) { @@ -314,5 +307,5 @@ draw_level :: proc() { rl.DrawCubeWiresV(towerPos, TOWER_SIZE, rl.DARKBLUE) // Red sun - rl.DrawSphere(rl.Vector3{300.0, 300.0, 0.0}, 100, rl.Color{255, 0, 0, 255}) + rl.DrawSphere({300.0, 300.0, 0.0}, 100, rl.Color{255, 0, 0, 255}) } \ No newline at end of file From 5f950a15ab466830617e8804f723dcf6e39f98cd Mon Sep 17 00:00:00 2001 From: GCodergr Date: Thu, 9 Oct 2025 10:09:35 +0300 Subject: [PATCH 5/5] Updated variable operations to use swizzling feature Also improved code formatting --- raylib/ports/core/core_3d_camera_fps.odin | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/raylib/ports/core/core_3d_camera_fps.odin b/raylib/ports/core/core_3d_camera_fps.odin index 9e68063..142cf5f 100644 --- a/raylib/ports/core/core_3d_camera_fps.odin +++ b/raylib/ports/core/core_3d_camera_fps.odin @@ -183,11 +183,7 @@ update_body :: proc(body: ^Body, rot: f32, side: i8, forward: i8, jumpPressed: b front := rl.Vector3{linalg.sin(rot), 0, linalg.cos(rot)} right := rl.Vector3{linalg.cos(-rot), 0, linalg.sin(-rot)} - desiredDir := rl.Vector3 { - input.x * right.x + input.y * front.x, - 0.0, - input.x * right.z + input.y * front.z, - } + desiredDir := rl.Vector3{input.x * right.x + input.y * front.x, 0.0, input.x * right.z + input.y * front.z} body.dir = linalg.lerp(body.dir, desiredDir, CONTROL * delta) decel : f32 = (body.isGrounded ? FRICTION : AIR_DRAG) @@ -206,11 +202,9 @@ update_body :: proc(body: ^Body, rot: f32, side: i8, forward: i8, jumpPressed: b // More info here: https://youtu.be/v3zT3Z5apaM?t=165 maxSpeed: f32 = (crouchHold ? CROUCH_SPEED : MAX_SPEED) accel := rl.Clamp(maxSpeed - speed, 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 + hvel.xz += body.dir.xz * accel + + body.velocity.xz = hvel.xz body.position += body.velocity * delta