From 29c88a67ad4b373152b1f73a5163c22f347a86a8 Mon Sep 17 00:00:00 2001 From: Jason Hilder Date: Tue, 29 Sep 2026 07:54:09 +0200 Subject: [PATCH] Setup 2D Camera for shake effect. --- src/game.odin | 57 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/src/game.odin b/src/game.odin index dd2ea06..83b9019 100644 --- a/src/game.odin +++ b/src/game.odin @@ -1,10 +1,9 @@ package game import "core:strings" +import "core:math/rand" import rl "vendor:raylib" -// VIEWPORT_W :: 1280 -// VIEWPORT_H :: 720 VIEWPORT_W :: 640 VIEWPORT_H :: 360 WINDOW_TITLE :: "Pong" @@ -31,6 +30,11 @@ Game :: struct { should_close: bool, audio: Audio_Manager, + // Camera state for screen effects / screen shake + camera: rl.Camera2D, + shake_intensity: f32, + shake_timer: f32, + // resolution state window_w: i32, window_h: i32, @@ -78,6 +82,14 @@ game_init :: proc(game: ^Game) { game.crt_shader = rl.LoadShader(nil, "assets/shaders/crt.fs") + // Setup 2D camera matching the 640x360 viewport center + game.camera = rl.Camera2D{ + target = rl.Vector2{VIEWPORT_W / 2.0, VIEWPORT_H / 2.0}, + offset = rl.Vector2{VIEWPORT_W / 2.0, VIEWPORT_H / 2.0}, + rotation = 0.0, + zoom = 1.0, + } + game.should_close = false set_root_scene(game, make_main_menu()) } @@ -86,6 +98,28 @@ game_update :: proc(game: ^Game) { dt := rl.GetFrameTime() input := poll_input() + // ------------------------------------------------------------------------- + // Update Camera Shake + // ------------------------------------------------------------------------- + base_center := rl.Vector2{VIEWPORT_W / 2.0, VIEWPORT_H / 2.0} + + if game.shake_timer > 0 { + game.shake_timer -= dt + + // Generate random offset within current intensity bounds + offset_x := (f32(rand.float32()) * 2.0 - 1.0) * game.shake_intensity + offset_y := (f32(rand.float32()) * 2.0 - 1.0) * game.shake_intensity + + game.camera.target = base_center + rl.Vector2{offset_x, offset_y} + + if game.shake_timer <= 0 { + game.shake_intensity = 0.0 + game.camera.target = base_center + } + } else { + game.camera.target = base_center + } + active_scene := get_active_scene(game) if active_scene != nil { scene_update(active_scene, game, input, dt) @@ -105,12 +139,16 @@ game_render :: proc(game: ^Game) { rl.BeginTextureMode(game.viewport) rl.DrawTexture(game.bg_texture, 0, 0, rl.WHITE) - // @note: fine for now but probably not a good idea... - // Render all scenes in the stack from bottom to top - for i in 0 ..< len(game.scene_stack) { - scene_render(&game.scene_stack[i]) - } + // World/Scene objects rendered through 2D camera space + rl.BeginMode2D(game.camera) + // @note: fine for now but probably not a good idea... + // Render all scenes in the stack from bottom to top + for i in 0 ..< len(game.scene_stack) { + scene_render(&game.scene_stack[i]) + } + + rl.EndMode2D() rl.EndTextureMode() render_game_to_window(game) @@ -136,6 +174,11 @@ game_should_run :: proc(game: Game) -> bool { return !game.should_close && !rl.WindowShouldClose() } +game_trigger_shake :: proc(game: ^Game, intensity: f32, duration: f32 = 0.15) { + game.shake_intensity = max(game.shake_intensity, intensity) + game.shake_timer = duration +} + // ------------------------------------------------------------------------------ // Window Upscale Render // ------------------------------------------------------------------------------