From eee9dd8c944331dd804752383692076ab4d2790e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agnis=20Aldi=C5=86=C5=A1=20=22NeZv=C4=93rs?= Date: Mon, 23 Jun 2025 13:03:35 +0300 Subject: [PATCH] Example: core_3d_fps_controller Quake like camera animations and strafe jump movement --- examples/Makefile | 1 + examples/Makefile.Web | 4 + examples/core/core_3d_fps_controller.c | 360 +++++++++++++++++++++++++ examples/core/resources/huh_jump.wav | Bin 0 -> 19050 bytes 4 files changed, 365 insertions(+) create mode 100644 examples/core/core_3d_fps_controller.c create mode 100644 examples/core/resources/huh_jump.wav diff --git a/examples/Makefile b/examples/Makefile index 32a3a75ab..edd2eb399 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -500,6 +500,7 @@ CORE = \ core/core_3d_camera_free \ core/core_3d_camera_mode \ core/core_3d_camera_split_screen \ + core/core_3d_fps_controller \ core/core_3d_picking \ core/core_automation_events \ core/core_basic_screen_manager \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 35ae70a18..5ba550eb0 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -382,6 +382,7 @@ CORE = \ core/core_3d_camera_free \ core/core_3d_camera_mode \ core/core_3d_camera_split_screen \ + core/core_3d_fps_controller \ core/core_3d_picking \ core/core_automation_events \ core/core_basic_screen_manager \ @@ -587,6 +588,9 @@ core/core_3d_camera_mode: core/core_3d_camera_mode.c core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_3d_fps_controller: core/core_3d_fps_controller.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_3d_picking: core/core_3d_picking.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) diff --git a/examples/core/core_3d_fps_controller.c b/examples/core/core_3d_fps_controller.c new file mode 100644 index 000000000..56d8c4543 --- /dev/null +++ b/examples/core/core_3d_fps_controller.c @@ -0,0 +1,360 @@ +/******************************************************************************************* +* +* raylib [core] example - Input Gestures for Web +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created 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-2025 Agnis Aldins (@nezvers) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" +#include "rcamera.h" + +//#define PLATFORM_WEB + +#if defined(PLATFORM_WEB) +#include +#endif + +#if defined(PLATFORM_DESKTOP) +#define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB +#define GLSL_VERSION 100 +#endif + + +/* Movement constants */ +#define GRAVITY 32.f +#define MAX_SPEED 20.f +#define CROUCH_SPEED 5.f +#define JUMP_FORCE 12.f +#define MAX_ACCEL 150.f +/* 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.f +#define CROUCH_HEIGHT 0.f +#define STAND_HEIGHT 1.f +#define BOTTOM_HEIGHT 0.5f + +#define NORMALIZE_INPUT 0 + +typedef struct { + Vector3 position; + Vector3 velocity; + Vector3 dir; + bool isGrounded; + Sound soundJump; +}Body; + +const int screenWidth = 1280; +const int screenHeight = 720; +Vector2 sensitivity = { 0.001f, 0.001f }; + +Body player; +Camera camera; +Vector2 lookRotation = { 0 }; +float headTimer; +float walkLerp; +float headLerp; +Vector2 lean; + +void UpdateDrawFrame(void); // Update and Draw one frame + +void DrawLevel(); + +void UpdateCameraAngle(Camera* camera, Vector2* rot, float headTimer, float walkLerp, Vector2 lean); + +void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed, bool crouchHold); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "Raylib Quake-like controller"); + InitAudioDevice(); + + player = (Body){ Vector3Zero(), Vector3Zero(), Vector3Zero(), false, LoadSound("resources/huh_jump.wav")}; + camera = (Camera){ 0 }; + camera.fovy = 60.f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + lookRotation = Vector2Zero(); + headTimer = 0.f; + walkLerp = 0.f; + headLerp = STAND_HEIGHT; + lean = Vector2Zero(); + + camera.position = (Vector3){ + player.position.x, + player.position.y + (BOTTOM_HEIGHT + headLerp), + player.position.z, + }; + UpdateCameraAngle(&camera, &lookRotation, headTimer, walkLerp, lean); + + DisableCursor(); // Limit cursor to relative movement inside the window + +#if defined(PLATFORM_WEB) + emscripten_set_main_loop(UpdateDrawFrame, 0, 1); +#else + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + UpdateDrawFrame(); + } +#endif + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadSound(player.soundJump); + CloseAudioDevice(); + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +void UpdateDrawFrame(void) +{ + // Update + //---------------------------------------------------------------------------------- + + Vector2 mouse_delta = GetMouseDelta(); + lookRotation.x -= mouse_delta.x * sensitivity.x; + lookRotation.y += mouse_delta.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.f * 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.f; + walkLerp = Lerp(walkLerp, 1.f, 10.f * delta); + camera.fovy = Lerp(camera.fovy, 55.f, 5.f * delta); + } + else { + walkLerp = Lerp(walkLerp, 0.f, 10.f * delta); + camera.fovy = Lerp(camera.fovy, 60.f, 5.f * delta); + } + + lean.x = Lerp(lean.x, sideway * 0.02f, 10.f * delta); + lean.y = Lerp(lean.y, forward * 0.015f, 10.f * delta); + + UpdateCameraAngle(&camera, &lookRotation, headTimer, walkLerp, lean); + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawLevel(); + + EndMode3D(); + + // Draw info box + 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(TextFormat("- Velocity Len: (%06.3f)", Vector2Length((Vector2) { player.velocity.x, player.velocity.z })), 15, 60, 10, BLACK); + + + EndDrawing(); + //---------------------------------------------------------------------------------- +} + +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; + SetSoundPitch(body->soundJump, 1.f + (GetRandomValue(-100, 100) * 0.001)); + PlaySound(body->soundJump); + } + + Vector3 front_vec = (Vector3){ sin(rot), 0.f, cos(rot) }; + Vector3 right_vec = (Vector3){ cos(-rot), 0.f, sin(-rot) }; + + Vector3 desired_dir = (Vector3){ + input.x * right_vec.x + input.y * front_vec.x, + 0.f, + input.x * right_vec.z + input.y * front_vec.z, + }; + + body->dir = Vector3Lerp(body->dir, desired_dir, CONTROL * delta); + + float decel = body->isGrounded ? FRICTION : AIR_DRAG; + Vector3 hvel = (Vector3){ + body->velocity.x * decel, + 0.f, + body->velocity.z * decel + }; + + float hvel_length = Vector3Length(hvel); // a.k.a. magnitude + if (hvel_length < 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 max_speed = crouchHold ? CROUCH_SPEED : MAX_SPEED; + float accel = Clamp(max_speed - 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.f) + { + body->position.y = 0.f; + body->velocity.y = 0.f; + body->isGrounded = true; // <= enables jumping + } +} + +void UpdateCameraAngle(Camera* camera, Vector2* rot, float headTimer, float walkLerp, Vector2 lean) +{ + const Vector3 up = (Vector3){ 0.f, 1.f, 0.f }; + const Vector3 targetOffset = (Vector3){ 0.f, 0.f, -1.f }; + + /* Left & Right */ + Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, rot->x); + + // Clamp view up + float maxAngleUp = Vector3Angle(up, yaw); + maxAngleUp -= 0.001f; // avoid numerical errors + if ( -(rot->y) > maxAngleUp) { rot->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 ( -(rot->y) < maxAngleDown) { rot->y = -maxAngleDown; } + + /* Up & Down */ + Vector3 right = Vector3Normalize(Vector3CrossProduct(yaw, up)); + + // Rotate view vector around right axis + Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, -rot->y - lean.y); + + // Head animation + // Rotate up direction around forward axis + float _sin = sin(headTimer * PI); + float _cos = cos(headTimer * PI); + const float stepRotation = 0.01f; + camera->up = Vector3RotateByAxisAngle(up, pitch, _sin * stepRotation + lean.x); + + /* BOB */ + const float bobSide = 0.1f; + const float bobUp = 0.15f; + Vector3 bobbing = Vector3Scale(right, _sin * bobSide); + bobbing.y = fabsf(_cos * bobUp); + camera->position = Vector3Add(camera->position, Vector3Scale(bobbing, walkLerp)); + + camera->target = Vector3Add(camera->position, pitch); +} + + +void DrawLevel() +{ + const int floorExtent = 25; + const float tileSize = 5.f; + 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.f, y * tileSize}, + (Vector2) {tileSize, tileSize}, tileColor1); + } + else if(!(y & 1) && !(x & 1)) + { + DrawPlane((Vector3) { x * tileSize, 0.f, y * tileSize}, + (Vector2) {tileSize, tileSize}, LIGHTGRAY); + } + } + } + + const Vector3 towerSize = (Vector3){ 16.f, 32.f, 16.f }; + const Color towerColor = (Color){ 150, 200, 200, 255 }; + + Vector3 towerPos = (Vector3){ 16.f, 16.f, 16.f }; + 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); +} + diff --git a/examples/core/resources/huh_jump.wav b/examples/core/resources/huh_jump.wav new file mode 100644 index 0000000000000000000000000000000000000000..9ca8a2e683ef87d19f594be54d04a40dd0bade10 GIT binary patch literal 19050 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