From ec64a3358c02b3d9fd38be72524c74236dd0bd4c Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Mon, 4 Aug 2025 21:57:11 -0700 Subject: [PATCH] We have (slightly) more modular environments --- src/bin/flappy/main.rs | 213 ++++++++++++++++++++++++----------------- 1 file changed, 124 insertions(+), 89 deletions(-) diff --git a/src/bin/flappy/main.rs b/src/bin/flappy/main.rs index d70eff0..42179d1 100644 --- a/src/bin/flappy/main.rs +++ b/src/bin/flappy/main.rs @@ -16,6 +16,8 @@ fn main() { Physics2dPlugin, )) .insert_resource(Gravity(Vec2::NEG_Y * 9.8 * 100.0)) + .init_resource::() + .init_resource::() .init_resource::() .init_resource::() .init_resource::() @@ -25,7 +27,9 @@ fn main() { Startup, ( init_bird, - init_obstacles, + // init_obstacles, + init_obstacle_assets, + init_first_batches.after(init_obstacle_assets), init_ui, tweak_camera.after(create_camera_2d), ), @@ -81,6 +85,10 @@ fn main() { ), ) .add_observer(flap) + .add_observer(populate_batch) + .add_observer(populate_pipe) + .add_observer(populate_ground) + .add_observer(populate_hitbox) .run(); } @@ -152,108 +160,135 @@ fn init_bird( } #[derive(Component, Clone)] -struct Ground; +struct Ground(isize); #[derive(Component, Clone)] -struct Pipe; +enum Pipe { + Top, + Bottom, +} #[derive(Component, Clone)] struct Hitbox; -fn init_obstacles( +#[derive(Component, Clone)] +struct Batch(usize); + +fn init_first_batches( + mut commands: Commands +) { + commands.spawn(Batch(0)); + commands.spawn(Batch(1)); + commands.spawn(Batch(2)); + commands.spawn(Batch(3)); + commands.spawn(Batch(4)); +} + +fn populate_batch( + trigger: Trigger, + batches: Query<&Batch>, mut commands: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, - server: Res, ) { - let ground = { - let material = MeshMaterial2d(materials.add(ColorMaterial { - texture: Some(server.load("kenny.nl/1-bit-platformer-pack/tile_0088.png")), - color: BLACK.into(), - ..default() - })); + let Batch(batch_id) = batches.get(trigger.target()).unwrap(); + commands + .entity(trigger.target()) + .insert((Transform::from_xyz(500.0 * (*batch_id) as f32, 0.0, 0.0), Visibility::Inherited)) + .with_children(|parent| { + parent.spawn(Ground(-2)); + parent.spawn(Ground(-1)); + parent.spawn(Ground(0)); + parent.spawn(Ground(1)); + parent.spawn(Ground(2)); + if *batch_id > 0 { + parent.spawn(Pipe::Top); + parent.spawn(Pipe::Bottom); + parent.spawn(Hitbox); + } + }); +} + +fn populate_ground( + trigger: Trigger, + grounds: Query<&Ground>, + ground_assets: Res, + mut commands: Commands, +) { + let Ground(idx) = grounds.get(trigger.target()).unwrap(); + commands.entity(trigger.target()).insert(( + ground_assets.material.clone(), + ground_assets.mesh.clone(), + Name::new("ground"), + RigidBody::Static, Collider::rectangle(1.0, 1.0), + Transform::from_xyz(100.0 * (*idx) as f32, -400.0, -1.0).with_scale(Vec3::splat(100.0)) + )); +} - let mesh = Mesh2d(meshes.add(Rectangle::new(1.0, 1.0))); +fn populate_pipe( + trigger: Trigger, + pipes: Query<&Pipe>, + pipe_assets: Res, + mut commands: Commands, +) { + let pipe_t = match pipes.get(trigger.target()).unwrap() { + Pipe::Top => Transform::from_xyz(0.0, 200.0, -1.0).with_scale(Vec3::splat(100.0)), + Pipe::Bottom => Transform::from_xyz(0.0, -200.0, -1.0).with_scale(Vec3::splat(100.0)), + }; + commands.entity(trigger.target()).insert(( + pipe_t, + pipe_assets.material.clone(), + pipe_assets.mesh.clone(), + RigidBody::Static, Collider::rectangle(1.0, 1.0), + Name::new("pipe"), + )); +} - let name = Name::new("ground"); +fn populate_hitbox( + trigger: Trigger, + mut commands: Commands, +) { + commands.entity(trigger.target()).insert(( + RigidBody::Static, + Collider::rectangle(1.0, 10.0), + Sensor, + CollisionEventsEnabled, + Name::new("hitbox"), + Transform::from_xyz(0.0, 0.0, 0.0).with_scale(Vec3::splat(100.0)), + )); +} - let physics = (RigidBody::Static, Collider::rectangle(1.0, 1.0)); +#[derive(Resource, Default)] +struct GroundComponents { + material: MeshMaterial2d, + mesh: Mesh2d, +} - (name, mesh, material, physics, Ground) - }; - let pipe = { - let material = MeshMaterial2d(materials.add(ColorMaterial { - texture: Some(server.load("kenny.nl/1-bit-platformer-pack/tile_0247.png")), - color: GREEN.into(), - ..default() - })); +#[derive(Resource, Default)] +struct PipeComponents { + material: MeshMaterial2d, + mesh: Mesh2d, +} - let mesh = Mesh2d(meshes.add(Rectangle::new(1.0, 1.0))); - let physics = (RigidBody::Static, Collider::rectangle(1.0, 1.0)); - let name = Name::new("pipe"); +fn init_obstacle_assets( + mut meshes: ResMut>, + mut materials: ResMut>, + mut pipe_assets: ResMut, + mut ground_assets: ResMut, + server: Res, +) { + pipe_assets.material = MeshMaterial2d(materials.add(ColorMaterial { + texture: Some(server.load("kenny.nl/1-bit-platformer-pack/tile_0247.png")), + color: GREEN.into(), + ..default() + })); + pipe_assets.mesh = Mesh2d(meshes.add(Rectangle::new(1.0, 1.0))); - ( - name.clone(), - mesh.clone(), - material.clone(), - physics.clone(), - Pipe, - ) - }; - let hitbox = { - let physics = ( - RigidBody::Static, - Collider::rectangle(1.0, 10.0), - Sensor, - CollisionEventsEnabled, - ); - let name = Name::new("hitbox"); - - (name.clone(), physics.clone(), Hitbox) - }; + ground_assets.material = MeshMaterial2d(materials.add(ColorMaterial { + texture: Some(server.load("kenny.nl/1-bit-platformer-pack/tile_0088.png")), + color: BLACK.into(), + ..default() + })); - // TODO: Instead of spawning infinite floor/pipes, we should instead spawn enough for 1-3 a few - // screens and then "move" them around. - // This is considerably more complexity so can be implemented later, but would keep memory - // overhead fairly static. - (1..10).for_each(|i| { - // TODO: Jitter close/far of pipes for challenge - let jitter: f32 = { - let h = RandomState::default(); - let x: u64 = h.hash_one(i); - ((x % 10) * 20) as f32 - 100.0 - }; - info!("Jitter for {i} is {jitter}"); - let above = { - let x = 300.0 * i as f32; - let y = f32::min(300.0 + jitter, 400.0); - Transform::from_xyz(x, y, -1.0).with_scale(Vec3::splat(100.0)) - }; - commands.spawn((pipe.clone(), above)); - - let below = { - let x = 300.0 * i as f32; - let y = f32::max(-200.0 + jitter, -300.0); - Transform::from_xyz(x, y, -1.0).with_scale(Vec3::splat(100.0)) - }; - commands.spawn((pipe.clone(), below)); - - let hitbox_pos = - Transform::from_xyz(300.0 * i as f32, 0.0, 10.0).with_scale(Vec3::splat(100.0)); - commands.spawn((hitbox_pos, hitbox.clone())); - - let floor1 = - Transform::from_xyz(300.0 * i as f32 - 100.0, -300.0, 1.0).with_scale(Vec3::splat(100.0)); - commands.spawn((ground.clone(), floor1)); - - let floor2 = - Transform::from_xyz(300.0 * i as f32, -300.0, 1.0).with_scale(Vec3::splat(100.0)); - commands.spawn((ground.clone(), floor2)); - - let floor3 = - Transform::from_xyz(300.0 * i as f32 + 100.0, -300.0, 1.0).with_scale(Vec3::splat(100.0)); - commands.spawn((ground.clone(), floor3)); - }); + ground_assets.mesh = Mesh2d(meshes.add(Rectangle::new(1.0, 1.0))); } fn init_ui(mut commands: Commands) { @@ -390,7 +425,7 @@ fn flap( mut bird: Query<&mut ExternalImpulse, With>, mut flaps: ResMut, ) { - info!("real flap for {:?}", trigger.target()); + debug!("real flap for {:?}", trigger.target()); // Increment flap stat flaps.0 += 1;