From 48e6b9ff614481dafca54ac16c2d641c6de601be Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Wed, 14 Jul 2021 16:06:26 -0500 Subject: [PATCH] Minimize number of colliders by tracing walls. --- src/map.rs | 144 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 126 insertions(+), 18 deletions(-) diff --git a/src/map.rs b/src/map.rs index f4284e5..507f1f5 100644 --- a/src/map.rs +++ b/src/map.rs @@ -34,6 +34,10 @@ pub struct MapObstruction; #[reflect(Component)] pub struct Portal; +#[derive(Clone, Debug, Default, Deref, DerefMut, Reflect)] +#[reflect(Component)] +pub struct SpawnColliderPerTile(pub bool); + #[derive(Clone, Debug, Deref, DerefMut, Reflect)] #[reflect(Component)] pub struct SpawnColliders(pub bool); @@ -113,6 +117,7 @@ impl Default for PortalBundle { pub struct MapBundle { pub map: Map, pub spawn_colliders: SpawnColliders, + pub spawn_collider_per_tile: SpawnColliderPerTile, pub spawn_portals: SpawnPortals, pub children: Children, pub transform: Transform, @@ -193,32 +198,135 @@ impl MapFilter for GridBuilder { fn spawn_colliders( mut commands: Commands, - maps: Query<(Entity, &Map, &SpawnColliders), Changed>, + maps: Query<(Entity, &Map, &SpawnColliders, &SpawnColliderPerTile), Changed>, ) { - for (map_entity, map, spawn_colliders) in maps.iter() { + for (map_entity, map, spawn_colliders, spawn_collider_per_tile) in maps.iter() { if **spawn_colliders { commands.entity(map_entity).remove::(); + commands.entity(map_entity).remove::(); commands.entity(map_entity).insert_bundle(RigidBodyBundle { body_type: RigidBodyType::Static, ..Default::default() }); - for x in 0..map.width { + if **spawn_collider_per_tile { for y in 0..map.height { - let tile = map.at(x, y); - if tile.blocks_motion() { - let id = commands - .spawn_bundle(ColliderBundle { - shape: ColliderShape::cuboid(0.5, 0.5), - ..Default::default() - }) - .insert(ColliderParent { - handle: map_entity.handle(), - pos_wrt_parent: Vec2::new(x as f32 + 0.5, y as f32 + 0.5).into(), - }) - .insert(MapObstruction) - .id(); - if tile.blocks_visibility() { - commands.entity(id).insert(BlocksVisibility); + for x in 0..map.width { + let tile = map.at(x, y); + if tile.blocks_motion() { + let id = commands + .spawn_bundle(ColliderBundle { + shape: ColliderShape::cuboid(0.5, 0.5), + ..Default::default() + }) + .insert(ColliderParent { + handle: map_entity.handle(), + pos_wrt_parent: Vec2::new(x as f32 + 0.5, y as f32 + 0.5) + .into(), + }) + .insert(MapObstruction) + .id(); + if tile.blocks_visibility() { + commands.entity(id).insert(BlocksVisibility); + } + } + } + } + } else { + let mut has_body = vec![vec![false; map.height as usize]; map.width as usize]; + let mut bottom_left = None; + let mut bottom_right = None; + for y in 0..map.height { + for x in 0..map.width { + // println!("Checking ({}, {})", x, y); + if bottom_left.is_some() { + if has_body[x][y] { + // println!("Hit another body, setting bottom right"); + bottom_right = Some((x, y)); + } else if map.at(x, y).is_walkable() { + /*println!( + "Hit an empty tile, setting bottom right to ({}, {})", + x - 1, + y + );*/ + bottom_right = Some((x - 1, y)); + } + } + if map.at(x, y).is_blocked() && !has_body[x][y] { + //println!("Blocked, setting has_body"); + has_body[x][y] = true; + if bottom_left.is_none() { + //println!("Setting bottom left"); + bottom_left = Some((x, y)); + } + if bottom_left.is_some() && x == map.width - 1 { + //println!("Hit right edge, setting bottom right"); + bottom_right = Some((x, y)); + } + } + if let (Some(bl), Some(br)) = (bottom_left, bottom_right) { + //println!("Got bottom, checking if can extend up"); + let mut top_left = bl.clone(); + let mut top_right = br.clone(); + if y != map.height - 1 { + let mut can_extend_up = true; + for y in bl.1 + 1..map.height { + for x in bl.0..=br.0 { + //println!("Extension check: ({}, {})", x, y); + if map.at(x, y).is_walkable() { + //println!("Can't, empty tile"); + can_extend_up = false; + break; + } + } + if can_extend_up { + //println!("Can extend up, setting has_body"); + top_left.1 += 1; + top_right.1 += 1; + for x in top_left.0..=top_right.0 { + has_body[x][top_left.1] = true; + } + } else { + break; + } + } + } + let mut width = br.0 as f32 - bl.0 as f32; + if width == 0. { + width = 1.; + } + let half_width = width / 2.; + let mut height = top_left.1 as f32 - bl.1 as f32; + if height == 0. { + height = 1.; + } + let half_height = height / 2.; + /*println!( + "Top left: {:?}\ntop right: {:?}\nbottom left: {:?}\nbottom right: {:?}", + top_left, top_right, bl, br + );*/ + let center = (bl.0 as f32 + half_width, br.1 as f32 + half_height); + let x = center.0; + let y = center.1; + /*println!( + "Create shape at {:?} of width {} and height {}", + center, width, height + );*/ + let id = commands + .spawn_bundle(ColliderBundle { + shape: ColliderShape::cuboid(half_width, half_height), + ..Default::default() + }) + .insert(ColliderParent { + handle: map_entity.handle(), + pos_wrt_parent: Vec2::new(x, y).into(), + }) + .insert(MapObstruction) + .id(); + if map.at(x as usize, y as usize).blocks_visibility() { + commands.entity(id).insert(BlocksVisibility); + } + bottom_left = None; + bottom_right = None; } } }