//! 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(( Text::new("Hold space to trigger a screen shake"), Node { 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