Minimize number of colliders by tracing walls.

This commit is contained in:
Nolan Darilek 2021-07-14 16:06:26 -05:00
parent 3de4b69030
commit 48e6b9ff61

View File

@ -34,6 +34,10 @@ pub struct MapObstruction;
#[reflect(Component)] #[reflect(Component)]
pub struct Portal; pub struct Portal;
#[derive(Clone, Debug, Default, Deref, DerefMut, Reflect)]
#[reflect(Component)]
pub struct SpawnColliderPerTile(pub bool);
#[derive(Clone, Debug, Deref, DerefMut, Reflect)] #[derive(Clone, Debug, Deref, DerefMut, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct SpawnColliders(pub bool); pub struct SpawnColliders(pub bool);
@ -113,6 +117,7 @@ impl Default for PortalBundle {
pub struct MapBundle { pub struct MapBundle {
pub map: Map, pub map: Map,
pub spawn_colliders: SpawnColliders, pub spawn_colliders: SpawnColliders,
pub spawn_collider_per_tile: SpawnColliderPerTile,
pub spawn_portals: SpawnPortals, pub spawn_portals: SpawnPortals,
pub children: Children, pub children: Children,
pub transform: Transform, pub transform: Transform,
@ -193,17 +198,19 @@ impl MapFilter for GridBuilder {
fn spawn_colliders( fn spawn_colliders(
mut commands: Commands, mut commands: Commands,
maps: Query<(Entity, &Map, &SpawnColliders), Changed<SpawnColliders>>, maps: Query<(Entity, &Map, &SpawnColliders, &SpawnColliderPerTile), Changed<SpawnColliders>>,
) { ) {
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 { if **spawn_colliders {
commands.entity(map_entity).remove::<SpawnColliders>(); commands.entity(map_entity).remove::<SpawnColliders>();
commands.entity(map_entity).remove::<SpawnColliderPerTile>();
commands.entity(map_entity).insert_bundle(RigidBodyBundle { commands.entity(map_entity).insert_bundle(RigidBodyBundle {
body_type: RigidBodyType::Static, body_type: RigidBodyType::Static,
..Default::default() ..Default::default()
}); });
for x in 0..map.width { if **spawn_collider_per_tile {
for y in 0..map.height { for y in 0..map.height {
for x in 0..map.width {
let tile = map.at(x, y); let tile = map.at(x, y);
if tile.blocks_motion() { if tile.blocks_motion() {
let id = commands let id = commands
@ -213,7 +220,8 @@ fn spawn_colliders(
}) })
.insert(ColliderParent { .insert(ColliderParent {
handle: map_entity.handle(), handle: map_entity.handle(),
pos_wrt_parent: Vec2::new(x as f32 + 0.5, y as f32 + 0.5).into(), pos_wrt_parent: Vec2::new(x as f32 + 0.5, y as f32 + 0.5)
.into(),
}) })
.insert(MapObstruction) .insert(MapObstruction)
.id(); .id();
@ -223,6 +231,106 @@ fn spawn_colliders(
} }
} }
} }
} 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;
}
}
}
}
for room in &map.rooms { for room in &map.rooms {
let shape = let shape =
ColliderShape::cuboid((room.width() / 2) as f32, (room.height() / 2) as f32); ColliderShape::cuboid((room.width() / 2) as f32, (room.height() / 2) as f32);