From 91bed8ce510efe8cdb265789faf41102fbdaaab8 Mon Sep 17 00:00:00 2001 From: Mohamed Osama <67656249+moOsama76@users.noreply.github.com> Date: Tue, 8 Oct 2024 00:14:07 +0300 Subject: [PATCH] Screen shake example (#15567) # Objective Closes https://github.com/bevyengine/bevy/issues/15564 ## Solution Adds a screen shake example. --------- Co-authored-by: Alice Cecile --- Cargo.toml | 12 ++ examples/README.md | 1 + examples/camera/2d_screen_shake.rs | 180 +++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 examples/camera/2d_screen_shake.rs diff --git a/Cargo.toml b/Cargo.toml index e5c0078e2299b..ecc1efedab575 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3476,6 +3476,18 @@ description = "Shows how to orbit a static scene using pitch, yaw, and roll." category = "Camera" wasm = true +[[example]] +name = "2d_screen_shake" +path = "examples/camera/2d_screen_shake.rs" +doc-scrape-examples = true + +[package.metadata.example.2d_screen_shake] +name = "Screen Shake" +description = "A simple 2D screen shake effect" +category = "Camera" +wasm = true + + [package.metadata.example.fps_overlay] name = "FPS overlay" description = "Demonstrates FPS overlay" diff --git a/examples/README.md b/examples/README.md index 15349d35ef5dc..1744816a8c914 100644 --- a/examples/README.md +++ b/examples/README.md @@ -269,6 +269,7 @@ Example | Description [Camera Orbit](../examples/camera/camera_orbit.rs) | Shows how to orbit a static scene using pitch, yaw, and roll. [First person view model](../examples/camera/first_person_view_model.rs) | A first-person camera that uses a world model and a view model with different field of views (FOV) [Projection Zoom](../examples/camera/projection_zoom.rs) | Shows how to zoom orthographic and perspective projection cameras. +[Screen Shake](../examples/camera/2d_screen_shake.rs) | A simple 2D screen shake effect ## Dev tools diff --git a/examples/camera/2d_screen_shake.rs b/examples/camera/2d_screen_shake.rs new file mode 100644 index 0000000000000..e66335f94bcb8 --- /dev/null +++ b/examples/camera/2d_screen_shake.rs @@ -0,0 +1,180 @@ +//! This example showcases a 2D screen shake using concept in this video: `` +//! +//! ## Controls +//! +//! | Key Binding | Action | +//! |:-------------|:---------------------| +//! | Space | Trigger screen shake | + +use bevy::{prelude::*, render::camera::SubCameraView, sprite::MeshMaterial2d}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; + +const CAMERA_DECAY_RATE: f32 = 0.9; // Adjust this for smoother or snappier decay +const TRAUMA_DECAY_SPEED: f32 = 0.5; // How fast trauma decays +const TRAUMA_INCREMENT: f32 = 1.0; // Increment of trauma per frame when holding space + +// screen_shake parameters, maximum addition by frame not actual maximum overall values +const MAX_ANGLE: f32 = 0.5; +const MAX_OFFSET: f32 = 500.0; + +#[derive(Component)] +struct Player; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, (setup_scene, setup_instructions, setup_camera)) + .add_systems(Update, (screen_shake, trigger_shake_on_space)) + .run(); +} + +fn setup_scene( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // World where we move the player + commands.spawn(( + Mesh2d(meshes.add(Rectangle::new(1000., 700.))), + MeshMaterial2d(materials.add(Color::srgb(0.2, 0.2, 0.3))), + )); + + // Player + commands.spawn(( + Player, + Mesh2d(meshes.add(Rectangle::new(50.0, 100.0))), // Rectangle size (width, height) + MeshMaterial2d(materials.add(Color::srgb(0.25, 0.94, 0.91))), // RGB values must be in range 0.0 to 1.0 + Transform::from_xyz(0., 0., 2.), + )); + + commands.spawn(( + Mesh2d(meshes.add(Rectangle::new(50.0, 50.0))), // Rectangle size (width, height) + MeshMaterial2d(materials.add(Color::srgb(0.85, 0.0, 0.2))), // RGB values must be in range 0.0 to 1.0 + Transform::from_xyz(-450.0, 200.0, 2.), + )); + + commands.spawn(( + Mesh2d(meshes.add(Rectangle::new(70.0, 50.0))), // Rectangle size (width, height) + MeshMaterial2d(materials.add(Color::srgb(0.5, 0.8, 0.2))), // RGB values must be in range 0.0 to 1.0 + Transform::from_xyz(450.0, -150.0, 2.), + )); + commands.init_resource::(); +} + +fn setup_instructions(mut commands: Commands) { + commands.spawn( + TextBundle::from_section("Hold space to trigger a screen shake", TextStyle::default()) + .with_style(Style { + position_type: PositionType::Absolute, + bottom: Val::Px(12.0), + left: Val::Px(12.0), + ..default() + }), + ); +} + +fn setup_camera(mut commands: Commands) { + commands.spawn(( + Camera2d, + Camera { + sub_camera_view: Some(SubCameraView { + full_size: UVec2::new(1000, 700), + offset: Vec2::new(0.0, 0.0), + size: UVec2::new(1000, 700), + }), + order: 1, + ..default() + }, + )); +} + +#[derive(Resource, Clone)] +struct ScreenShake { + max_angle: f32, + max_offset: f32, + trauma: f32, + latest_position: Option, +} + +impl Default for ScreenShake { + fn default() -> Self { + Self { + max_angle: 0.0, + max_offset: 0.0, + trauma: 0.0, + latest_position: Some(Vec2::default()), + } + } +} + +impl ScreenShake { + fn start_shake(&mut self, max_angle: f32, max_offset: f32, trauma: f32, final_position: Vec2) { + self.max_angle = max_angle; + self.max_offset = max_offset; + self.trauma = trauma.clamp(0.0, 1.0); + self.latest_position = Some(final_position); + } +} + +fn trigger_shake_on_space( + time: Res