From 830b29788e08f4d4680e6b4d9718eae15fcb70e7 Mon Sep 17 00:00:00 2001 From: Gui Date: Sat, 30 Aug 2025 09:00:26 +0100 Subject: [PATCH] Add new 3d camera shake example --- examples/README.md | 1 + examples/core/core_3d_camera_shake.c | 127 +++++++++++++++++++++++++ examples/core/core_3d_camera_shake.png | Bin 0 -> 13185 bytes 3 files changed, 128 insertions(+) create mode 100644 examples/core/core_3d_camera_shake.c create mode 100644 examples/core/core_3d_camera_shake.png diff --git a/examples/README.md b/examples/README.md index b391732e9..a920ba2e0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -38,6 +38,7 @@ Examples using raylib[core](../src/rcore.c) platform functionality like window c | [core_2d_camera_split_screen](core/core_2d_camera_split_screen.c) | core_2d_camera_split_screen | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [Gabriel dos Santos Sanches](https://github.com/gabrielssanches) | | [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | | [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐☆☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_3d_camera_shake](core/core_3d_camera_shake.c) | core_3d_camera_shake | ⭐⭐⭐☆ | 5.5 | 5.5 | [Guilherme Silva](https://github.com/grssilva) | | [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐⭐☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | | [core_3d_camera_split_screen](core/core_3d_camera_split_screen.c) | core_3d_camera_split_screen | ⭐⭐⭐☆ | 3.7 | 4.0 | [Jeffery Myers](https://github.com/JeffM2501) | | [core_3d_camera_fps](core/core_3d_camera_fps.c) | core_3d_camera_fps | ⭐⭐⭐☆ | 5.5 | 5.5 | [Agnis Aldins](https://github.com/nezvers) | diff --git a/examples/core/core_3d_camera_shake.c b/examples/core/core_3d_camera_shake.c new file mode 100644 index 000000000..89f454780 --- /dev/null +++ b/examples/core/core_3d_camera_shake.c @@ -0,0 +1,127 @@ +/******************************************************************************************* +* +* raylib [core] example - 3d camera shake +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* 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 Guilherme Silva (@grssilva) +* +********************************************************************************************/ + +#include "raylib.h" +#include "rcamera.h" + +#define MAX_YAW 10.0f // yaw is rotation around the y axis +#define MAX_PITCH 5.0f // pitch is rotation around the x axis +#define MAX_ROLL 20.0f // roll is rotation around the z axis +#define MAX_SCALE 1.5f // max scale is zoom in/out + +static Camera camera = { 0 }; +static float cameraTrauma = 0.0f; + +// Returns a random float between -1.0 and 1.0 +inline static float RandomFloat() { + return (float) GetRandomValue(-1000, 1000) / 1000.0f; +} + +void UpdateCameraTrauma(void) { + // Update camera shake + if (cameraTrauma > 1.0f) cameraTrauma = 1.0f; + else if (cameraTrauma < 0.01f) cameraTrauma = 0.0f; + else if (cameraTrauma == 0.0f) return; + + // Calculate the shake offsets + float yaw = MAX_YAW * RandomFloat() * cameraTrauma; + float pitch = MAX_PITCH * RandomFloat() * cameraTrauma; + float roll = MAX_ROLL * RandomFloat() * cameraTrauma; + float scale = 1.0f + (MAX_SCALE - 1.0f) * RandomFloat() * cameraTrauma; + + // Camera rotation, based on UpdateCameraPro implementation + const bool lockView = true; + const bool rotateAroundTarget = false; + const bool rotateUp = false; + + CameraPitch(&camera, -pitch * DEG2RAD, lockView, rotateAroundTarget, rotateUp); + CameraYaw(&camera, -yaw * DEG2RAD, rotateAroundTarget); + CameraRoll(&camera, roll * DEG2RAD); + + camera.fovy *= scale; + + // Degrade the camera trauma to create the smooth shake effect + cameraTrauma *= 0.95f; +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) { + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + // Seed the random number generator + SetRandomSeed((unsigned int) time(NULL)); + + InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera shake"); + + // Define the camera projection + camera.projection = CAMERA_PERSPECTIVE; // Camera mode type + + Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; + + 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 + + // Set the camera position and target + // Note: We need to set it every frame because the camera shake modifies it + camera.position = (Vector3) { 10.0f, 10.0f, 10.0f }; // Camera position + camera.target = (Vector3) { 0.0f, 0.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3) { 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + + // Set the camera shake to 1.0f when pressing SPACE + if (IsKeyPressed(KEY_SPACE)) cameraTrauma = 1.0f; + + UpdateCameraTrauma(); + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, BLUE); + DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, MAROON); + + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawText("Press SPACE to shake the camera!", 10, 40, 20, DARKGRAY); + DrawText(TextFormat("Camera shake: %.0f", cameraTrauma * 100), 10, 60, 20, DARKGRAY); + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/core/core_3d_camera_shake.png b/examples/core/core_3d_camera_shake.png new file mode 100644 index 0000000000000000000000000000000000000000..0a090bbe8d579fea17fc8daf17c849a78a0e577a GIT binary patch literal 13185 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 B?@s^# literal 0 HcmV?d00001