From 96581507a198e4ecb7569194fcd5c6b9c600705f Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Mon, 8 Jan 2024 17:56:16 -0600 Subject: [PATCH 01/12] rework camera to bound entities with a marker component --- src/core/camera.rs | 58 +++++++++++++++++++++++----------------------- src/core/player.rs | 3 +++ 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/core/camera.rs b/src/core/camera.rs index 8e6ade06ed..b853e3475b 100644 --- a/src/core/camera.rs +++ b/src/core/camera.rs @@ -22,17 +22,24 @@ pub struct ParallaxBackgroundSprite { pub meta: ParallaxLayerMeta, } +/// A subject of the camera. +/// +/// The camera will move and zoom to ensure that all subjects remain visible. Entities must also +/// have a `Transform` component and a `KinematicBody` component for this to work properly. +#[derive(Clone, Copy, Debug, Default, HasSchema)] +pub struct CameraSubject { + /// A rectangle around the subject that is larger than the subject, and will always move to + /// contain it. The camera will seek to contain this rectangle, instead of the subject itself. + /// + /// The advantage of this processing method is that the larger rect doesn't move as much as the + /// subject, because, for instance, a player jumping up and down in place will still be inside + /// of their camera rect, so the camera will not move around annoyingly. + rect: Rect, +} + /// The state of the camera. #[derive(Clone, Debug, HasSchema, Default)] pub struct CameraState { - /// A rectangle around the player that is larger than the player, and will always move to - /// contain the player. The camra will seek to contain this rectangle, instead of the player - /// itself. - /// - /// The advantage of this processing method is that the larger rect doesn't move as much as the - /// player, because, for instance, while jumping up and down in place, the player will still be - /// inside of their camera rect, so the camera will not move around annoyingly. - pub player_camera_rects: [Rect; MAX_PLAYERS], /// Disables the default camera controller. Useful, for example, when taking over the camera /// from the editor. pub disable_controller: bool, @@ -45,16 +52,16 @@ fn camera_controller( map: Res, mut cameras: CompMut, mut camera_shakes: CompMut, - mut camera_states: CompMut, + camera_states: Comp, + mut camera_subjects: CompMut, transforms: Comp, - player_indexes: Comp, bodies: Comp, window: Res, ) { let meta = &meta.core.camera; let Some((_ent, (camera, camera_shake, camera_state))) = entities - .iter_with((&mut cameras, &mut camera_shakes, &mut camera_states)) + .iter_with((&mut cameras, &mut camera_shakes, &camera_states)) .next() else { return; @@ -64,13 +71,13 @@ fn camera_controller( } // Update player camera rects - for (_ent, (transform, player_idx, body)) in - entities.iter_with((&transforms, &player_indexes, &bodies)) + for (_ent, (camera_subj, transform, body)) in + entities.iter_with((&mut camera_subjects, &transforms, &bodies)) { let camera_box_size = meta.player_camera_box_size; // Get the player's camera box - let camera_box = &mut camera_state.player_camera_rects[player_idx.0 as usize]; + let camera_box = &mut camera_subj.rect; // If it's not be initialized. if camera_box.min == Vec2::ZERO && camera_box.max == Vec2::ZERO { @@ -112,27 +119,19 @@ fn camera_controller( .unwrap_or(window.size); let viewport_aspect = viewport_size.x / viewport_size.y; let default_height = meta.default_height; - let camera_height = if let CameraSize::FixedHeight(height) = &camera.size { - *height + let default_width = viewport_aspect * default_height; + let camera_height = if let CameraSize::FixedHeight(height) = camera.size { + height } else { 400.0 }; let mut scale = camera_height / default_height; - let default_width = viewport_aspect * default_height; let map_size = map.grid_size.as_vec2() * map.tile_size; - let mut min = Vec2::new(f32::MAX, f32::MAX); - let mut max = Vec2::new(f32::MIN, f32::MIN); - - let players: Vec = entities - .iter_with(&player_indexes) - .map(|x| x.1 .0) - .collect(); - let player_count = players.len(); - - for player_idx in players { - let rect = camera_state.player_camera_rects[player_idx as usize]; + let mut min = Vec2::MAX; + let mut max = Vec2::MIN; + for CameraSubject { rect } in camera_subjects.iter_mut() { min = (rect.min - vec2(meta.border_left, meta.border_bottom)) .min(min) .max(Vec2::ZERO); @@ -142,7 +141,8 @@ fn camera_controller( let camera_pos = &mut camera_shake.center; - let mut middle_point = if player_count == 0 { + let subject_count = camera_subjects.iter().count(); + let mut middle_point = if subject_count == 0 { camera_pos.truncate() } else { Rect { min, max }.center() diff --git a/src/core/player.rs b/src/core/player.rs index ec205fd449..bffc4df40a 100644 --- a/src/core/player.rs +++ b/src/core/player.rs @@ -487,6 +487,7 @@ fn hydrate_players( mut animation_bank_sprites: CompMut, mut atlas_sprites: CompMut, mut kinematic_bodies: CompMut, + mut camera_subjects: CompMut, mut player_layers: CompMut, mut player_body_attachments: CompMut, mut transforms: CompMut, @@ -563,6 +564,8 @@ fn hydrate_players( ..default() }, ); + // debug!(?player_entity, "spawn player"); + camera_subjects.insert(player_entity, default()); // Spawn the player's fin and face let fin_entity = new_entities.next().unwrap(); From e717d5d3a6578fd8d99c63e5cab457604bafded0 Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Mon, 8 Jan 2024 17:56:37 -0600 Subject: [PATCH 02/12] make flappy jellyfish camera subjects --- src/core/elements/flappy_jellyfish.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/elements/flappy_jellyfish.rs b/src/core/elements/flappy_jellyfish.rs index b7fd223429..0690975b83 100644 --- a/src/core/elements/flappy_jellyfish.rs +++ b/src/core/elements/flappy_jellyfish.rs @@ -63,7 +63,8 @@ pub fn spawn(owner: Entity, jellyfish_ent: Entity) -> StaticSystem<(), ()> { assets: Res, mut atlas_sprites: CompMut, mut animated_sprites: CompMut, - mut transforms: CompMut| { + mut transforms: CompMut, + mut camera_subjects: CompMut| { let Some(flappy_meta) = element_handles .get(jellyfish_ent) .map(|element_h| assets.get(element_h.0)) @@ -114,6 +115,7 @@ pub fn spawn(owner: Entity, jellyfish_ent: Entity) -> StaticSystem<(), ()> { let mut transf = *transforms.get(owner).unwrap(); transf.translation += flappy_meta.spawn_offset.extend(0.0); transforms.insert(flappy_ent, transf); + camera_subjects.insert(flappy_ent, default()); }) .system() } From 4b40c8759c0c4b7ba84f71983c09ef062270830f Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Tue, 9 Jan 2024 19:32:05 -0600 Subject: [PATCH 03/12] reimpl flappy physics with `KinematicBody` --- src/core/elements/flappy_jellyfish.rs | 126 ++++++++++++-------------- 1 file changed, 57 insertions(+), 69 deletions(-) diff --git a/src/core/elements/flappy_jellyfish.rs b/src/core/elements/flappy_jellyfish.rs index 0690975b83..efe8cbaa6b 100644 --- a/src/core/elements/flappy_jellyfish.rs +++ b/src/core/elements/flappy_jellyfish.rs @@ -59,7 +59,6 @@ pub fn spawn(owner: Entity, jellyfish_ent: Entity) -> StaticSystem<(), ()> { mut driving_jellyfishes: CompMut, mut flappy_jellyfishes: CompMut, mut bodies: CompMut, - mut fall_velocities: CompMut, assets: Res, mut atlas_sprites: CompMut, mut animated_sprites: CompMut, @@ -97,11 +96,13 @@ pub fn spawn(owner: Entity, jellyfish_ent: Entity) -> StaticSystem<(), ()> { shape: ColliderShape::Rectangle { size: flappy_meta.body_size, }, - is_deactivated: true, + has_mass: true, + gravity: GRAVITY, + fall_through: true, + is_controlled: true, ..default() }, ); - fall_velocities.insert(flappy_ent, FallVelocity::default()); atlas_sprites.insert(flappy_ent, AtlasSprite::new(flappy_meta.atlas)); animated_sprites.insert( flappy_ent, @@ -131,6 +132,10 @@ fn explode_flappy_jellyfish( explode_flappies: Comp, killed_players: Comp, flappy_jellyfishes: Comp, + player_indexes: Comp, + invincibles: Comp, + bodies: Comp, + map: Res, mut driving_jellyfishes: CompMut, element_handles: Comp, assets: Res, @@ -142,22 +147,40 @@ fn explode_flappy_jellyfish( mut damage_regions: CompMut, mut lifetimes: CompMut, ) { - let mut explode_flappy_entities = Vec::with_capacity(flappy_jellyfishes.bitset().bit_count()); - - explode_flappy_entities.extend(entities.iter_with_bitset(explode_flappies.bitset())); - - explode_flappy_entities.extend( + // Collect the hitboxes of all players + let mut player_hitboxes = SmallVec::<[Rect; 8]>::with_capacity(8); + player_hitboxes.extend( entities - .iter_with(&flappy_jellyfishes) - .filter(|&(flappy_ent, flappy)| { - !explode_flappies.contains(flappy_ent) && killed_players.contains(flappy.owner) - }) - .map(|(e, _)| e), + .iter_with((&player_indexes, &transforms, &bodies)) + .filter(|(player_ent, _)| !invincibles.contains(*player_ent)) + .map(|(_, (_, transform, body))| body.bounding_box(*transform)), ); - for (flappy_ent, flappy_jellyfish) in entities.iter_with(&flappy_jellyfishes) { + let mut explode_flappy_entities = Vec::with_capacity(flappy_jellyfishes.bitset().bit_count()); + + for (flappy_ent, (flappy_jellyfish, transform, body)) in + entities.iter_with((&flappy_jellyfishes, &transforms, &bodies)) + { + // If flappy has the explode marker + if explode_flappies.contains(flappy_ent) { + explode_flappy_entities.push(flappy_ent); + continue; + } + // If the owner is dead if killed_players.contains(flappy_jellyfish.owner) { explode_flappy_entities.push(flappy_ent); + continue; + } + // If the flappy collides with any player + let flappy_hitbox = body.bounding_box(*transform); + if player_hitboxes.iter().any(|b| b.overlaps(&flappy_hitbox)) { + explode_flappy_entities.push(flappy_ent); + continue; + } + // If the flappy is out of bounds + if map.is_out_of_bounds(&transform.translation) { + explode_flappy_entities.push(flappy_ent); + continue; } } @@ -239,14 +262,11 @@ fn explode_flappy_jellyfish( } } -#[derive(Clone, Copy, Default, Deref, DerefMut, HasSchema)] -struct FallVelocity(f32); - -const SPEED_X: f32 = 200.0; -const SPEED_JUMP: f32 = 500.0; -const GRAVITY: f32 = -700.0; -const MAX_SPEED_Y: f32 = 300.0; -const MIN_SPEED_Y: f32 = -MAX_SPEED_Y; +const SPEED_X: f32 = 324.0; +const SPEED_JUMP: f32 = 3.5; +const GRAVITY: f32 = 0.1; +const MIN_SPEED: Vec2 = vec2(-SPEED_X, -4.0); +const MAX_SPEED: Vec2 = vec2(SPEED_X, 4.0); fn move_flappy_jellyfish( entities: Res, @@ -254,37 +274,20 @@ fn move_flappy_jellyfish( player_indexes: Comp, player_inputs: Res, mut commands: Commands, - bodies: Comp, - invincibles: Comp, + mut bodies: CompMut, time: Res