diff --git a/examples/parallax.rs b/examples/parallax.rs index 3b3595d..f12c8bd 100644 --- a/examples/parallax.rs +++ b/examples/parallax.rs @@ -10,22 +10,48 @@ fn main() { .add_systems(Startup, spawn_background) .add_systems(Update, move_camera) .add_systems(Update, parallax_gizmos) + .add_systems(Update, move_parallax_items) .run(); } fn spawn_background( mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, ) { - commands.spawn((Parallax(1.0), children![(Text2d("1.0".into()), Transform::from_xyz(0.0, 35.0, 0.0))])); - commands.spawn((Parallax(2.0), children![(Text2d("2.0".into()), Transform::from_xyz(0.0, 35.0, 0.0))])); - commands.spawn((Parallax(4.0), children![(Text2d("4.0".into()), Transform::from_xyz(0.0, 35.0, 0.0))])); - commands.spawn((Parallax(8.0), children![(Text2d("8.0".into()), Transform::from_xyz(0.0, 35.0, 0.0))])); + let mesh = Mesh2d(meshes.add(Circle::new(50.0))); + let material = MeshMaterial2d(materials.add(ColorMaterial::from_color(RED))); + commands.spawn(( + mesh.clone(), + material.clone(), + Parallax(1.0), + Visibility::Inherited, + children![(Text2d("1.0".into()), Transform::from_xyz(0.0, 35.0, 0.0))], + )); + commands.spawn(( + mesh.clone(), + material.clone(), + Parallax(2.0), + Visibility::Inherited, + children![(Text2d("2.0".into()), Transform::from_xyz(0.0, 35.0, 0.0))], + )); + commands.spawn(( + mesh.clone(), + material.clone(), + Parallax(4.0), + Visibility::Inherited, + children![(Text2d("4.0".into()), Transform::from_xyz(0.0, 35.0, 0.0))], + )); + commands.spawn(( + mesh.clone(), + material.clone(), + Parallax(8.0), + Visibility::Inherited, + children![(Text2d("8.0".into()), Transform::from_xyz(0.0, 35.0, 0.0))], + )); } -fn move_camera( - mut t: Single<&mut Transform, With>, - keys: Res>, -) { +fn move_camera(mut t: Single<&mut Transform, With>, keys: Res>) { if keys.pressed(KeyCode::ArrowLeft) { t.translation.x -= 5.0; } else if keys.pressed(KeyCode::ArrowRight) { @@ -38,18 +64,31 @@ fn move_camera( t.translation.y += 5.0; } } -fn parallax_gizmos( - mut gizmos: Gizmos, - q: Query<&Transform, With>, -) { + +fn parallax_gizmos(mut gizmos: Gizmos, q: Query<&Transform, With>) { // Closest to camera // Parallax(1) q.iter().for_each(|t| { - gizmos.grid_2d( - t.translation.truncate(), - UVec2::new(5, 5), - Vec2::splat(10.), - RED, - ).outer_edges(); + gizmos + .grid_2d( + t.translation.truncate(), + UVec2::new(5, 5), + Vec2::splat(10.), + RED, + ) + .outer_edges(); + }); +} + +// TODO: Move to src/parallax.rs +fn move_parallax_items( + mut q: Query<(Entity, &ViewVisibility, &mut ParallaxRepeatIteration), (With, Changed)>, +) { + q.iter_mut().for_each(|(e, vis, mut pri)| { + warn!("{e} {:?}", vis.get()); + if !vis.get() { + pri.0 += 1; + todo!("Works when moving camera right, make it work left too"); + } }); } diff --git a/src/bin/flappy/main.rs b/src/bin/flappy/main.rs index 7a69013..95fce32 100644 --- a/src/bin/flappy/main.rs +++ b/src/bin/flappy/main.rs @@ -150,7 +150,14 @@ impl Tape { } } - fn push(&mut self, lv: LinearVelocity, av: AngularVelocity, ei: ExternalImpulse, p: Position, r: Rotation) { + fn push( + &mut self, + lv: LinearVelocity, + av: AngularVelocity, + ei: ExternalImpulse, + p: Position, + r: Rotation, + ) { // If we are at capacity, make room if self.linear_velocities.len() == self.capacity { self.linear_velocities.pop_front().unwrap(); @@ -167,7 +174,15 @@ impl Tape { self.rotations.push_back(r); } - fn pop(&mut self) -> Option<(LinearVelocity, AngularVelocity, ExternalImpulse, Position, Rotation)> { + fn pop( + &mut self, + ) -> Option<( + LinearVelocity, + AngularVelocity, + ExternalImpulse, + Position, + Rotation, + )> { if self.linear_velocities.is_empty() { None } else { @@ -194,8 +209,7 @@ fn init_bird(mut commands: Commands, bird_assets: Res) { MaxLinearSpeed(500.0), ); - // 60fps * 60 seconds - // let tape = Tape::new_with_capacity(60 * 60); + // 60fps * 5 seconds const REWIND_SECONDS: usize = 5; let tape = Tape::new_with_capacity(60 * REWIND_SECONDS); @@ -611,8 +625,15 @@ fn init_ui(mut commands: Commands, server: Res) { RewindButton, children![ ( - ImageNode { color: BLACK.into(), image: rewind_image, ..default() }, - Node { height: Val::Px(50.0), ..default() }, + ImageNode { + color: BLACK.into(), + image: rewind_image, + ..default() + }, + Node { + height: Val::Px(50.0), + ..default() + }, ), ( Text::new("Rewind! (R)"), @@ -648,8 +669,15 @@ fn init_ui(mut commands: Commands, server: Res) { TextLayout::new_with_justify(JustifyText::Center) ), ( - ImageNode { color: BLACK.into(), image: play_image, ..default() }, - Node { height: Val::Px(50.0), ..default() }, + ImageNode { + color: BLACK.into(), + image: play_image, + ..default() + }, + Node { + height: Val::Px(50.0), + ..default() + }, ), ], )) @@ -667,7 +695,7 @@ fn init_ui(mut commands: Commands, server: Res) { SyncResource::::default(), Text::default(), TextLayout::new_with_justify(JustifyText::Center), - )] + )], )); } @@ -793,11 +821,9 @@ fn record( "Only record in the alive state" ); - birds - .iter_mut() - .for_each(|(lv, av, ei, p, r, mut tape)| { - tape.push(*lv, *av, *ei, *p, *r); - }); + birds.iter_mut().for_each(|(lv, av, ei, p, r, mut tape)| { + tape.push(*lv, *av, *ei, *p, *r); + }); } fn rewind( @@ -822,8 +848,9 @@ fn rewind( "Only rewind in the rewinding state" ); - birds.iter_mut().for_each( - |(mut lv, mut av, mut ei, mut p, mut r, mut tape)| { + birds + .iter_mut() + .for_each(|(mut lv, mut av, mut ei, mut p, mut r, mut tape)| { if let Some((new_lv, new_av, new_ei, new_p, new_r)) = tape.pop() { lv.0 = new_lv.0; av.0 = new_av.0; @@ -834,8 +861,7 @@ fn rewind( } else { next.set(PlayerState::Pause); } - }, - ); + }); } // PERF: May run more than necessary, should be event-driven on aabb intersection diff --git a/src/lib.rs b/src/lib.rs index 63ebc90..c161b32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,12 @@ mod base_game; mod debug; mod loading; +mod parallax; pub mod physics2d; pub mod physics3d; mod scheduling; mod ui; mod version; -mod parallax; // Rust stdlib pub use std::collections::VecDeque; @@ -42,7 +42,7 @@ pub use thiserror::Error; pub use base_game::*; pub use debug::*; pub use loading::*; +pub use parallax::*; pub use scheduling::*; pub use ui::*; pub use version::*; -pub use parallax::*; diff --git a/src/parallax.rs b/src/parallax.rs index 596b1f3..232b246 100644 --- a/src/parallax.rs +++ b/src/parallax.rs @@ -4,24 +4,47 @@ pub struct ParallaxPlugin; impl Plugin for ParallaxPlugin { fn build(&self, app: &mut App) { - app.add_systems(Update, move_parallax_items.run_if(any_component_changed::)); + app.add_systems( + Update, + move_parallax_items.run_if(any_component_changed::), + ); } } #[derive(Component)] -#[require(Transform)] +#[require(Transform, ParallaxRepeat, ParallaxRepeatIteration)] pub struct Parallax(pub f32); +#[derive(Component)] +pub struct ParallaxRepeat(pub f32); + +impl Default for ParallaxRepeat{ + fn default() -> Self { + ParallaxRepeat(1.0) + } +} + +#[derive(Component, Default)] +pub struct ParallaxRepeatIteration(pub isize); + fn move_parallax_items( - mut q: Query<(&mut Transform, &Parallax), Without>, - cam_t: Single<&Transform, With>, + mut q: Query<(&mut Transform, &Parallax, &ParallaxRepeat, &ParallaxRepeatIteration), Without>, + camera: Single<(&Camera, &Transform, &GlobalTransform), With>, + window: Single<&Window> ) { + let (cam, cam_t, cam_gt) = *camera; let base = cam_t.translation.truncate(); + let size = window.size(); - q.iter_mut().for_each(|(mut t, p)| { - let val = base * (1.0 - (1.0 / p.0)); + q.iter_mut().for_each(|(mut t, p, pr, pri)| { + let offset: Vec2 = { + let a = cam.viewport_to_world_2d(cam_gt, Vec2::new(0.0, size.y / 2.0)).unwrap(); + let b = cam.viewport_to_world_2d(cam_gt, Vec2::new(size.x, size.y / 2.0)).unwrap(); + (b - a) * pr.0 * (pri.0 as f32) + }; + // Something like add screen-dimensions * parallax repeat cycle number + let val = base * (1.0 - (1.0 / p.0)) + offset; t.translation.x = val.x; t.translation.y = val.y; }); } -