feat: reactive collision/sprite updates
parent
ff2ab7c3a2
commit
21255120c5
|
|
@ -627,6 +627,15 @@ dependencies = [
|
|||
"radsort",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_prototype_debug_lines"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d9e933f5bf971fbc7e83b447db6758ed3b091faf4cd8e524a9406ae7acca096"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_ptr"
|
||||
version = "0.9.0"
|
||||
|
|
@ -2071,6 +2080,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bevy",
|
||||
"bevy-inspector-egui",
|
||||
"bevy_prototype_debug_lines",
|
||||
"bevy_rapier2d",
|
||||
"lazy_static",
|
||||
"noise",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
bevy = { version = "0.9.0", features = ["dynamic"] }
|
||||
bevy-inspector-egui = "0.14.0"
|
||||
bevy_prototype_debug_lines = "0.9.0"
|
||||
bevy_rapier2d = { path = "../bevy_rapier/bevy_rapier2d" }
|
||||
lazy_static = "1.4.0"
|
||||
noise = "0.8.2"
|
||||
|
|
|
|||
17
src/game.rs
17
src/game.rs
|
|
@ -1,5 +1,6 @@
|
|||
use bevy::{input::mouse::MouseWheel, prelude::*};
|
||||
use bevy_inspector_egui::*;
|
||||
use bevy_prototype_debug_lines::DebugLinesPlugin;
|
||||
use bevy_rapier2d::prelude::*;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -14,13 +15,13 @@ use self::{
|
|||
};
|
||||
|
||||
pub mod camera;
|
||||
pub mod chunk;
|
||||
pub mod kinematic;
|
||||
pub mod player;
|
||||
|
||||
pub fn init() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(DebugLinesPlugin::default())
|
||||
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default())
|
||||
.add_plugin(RapierDebugRenderPlugin::default())
|
||||
.add_plugin(WorldInspectorPlugin::new())
|
||||
|
|
@ -53,11 +54,17 @@ fn setup_debug_terrain(mut commands: Commands, mut terrain: ResMut<Terrain2D>) {
|
|||
}
|
||||
}
|
||||
|
||||
commands.spawn(Name::new("Left wall"))
|
||||
commands
|
||||
.spawn(Name::new("Left wall"))
|
||||
.insert(Collider::halfspace(Vec2::X).unwrap())
|
||||
.insert(TransformBundle::from_transform(Transform::from_translation(Vec3::new(0.0, 0.0, 0.0))));
|
||||
.insert(TransformBundle::from_transform(
|
||||
Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
|
||||
));
|
||||
|
||||
commands.spawn(Name::new("Right wall"))
|
||||
commands
|
||||
.spawn(Name::new("Right wall"))
|
||||
.insert(Collider::halfspace(Vec2::NEG_X).unwrap())
|
||||
.insert(TransformBundle::from_transform(Transform::from_translation(Vec3::new(WORLD_WIDTH as f32, 0.0, 0.0))));
|
||||
.insert(TransformBundle::from_transform(
|
||||
Transform::from_translation(Vec3::new(WORLD_WIDTH as f32, 0.0, 0.0)),
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
use crate::terrain2d::Chunk2DIndex;
|
||||
|
||||
pub struct ChunkPlugin {}
|
||||
|
||||
#[derive(Reflect, Component, Default)]
|
||||
#[reflect(Component)]
|
||||
pub struct Chunk {
|
||||
pub index: Chunk2DIndex,
|
||||
}
|
||||
|
||||
#[derive(Bundle)]
|
||||
pub struct ChunkBundle {
|
||||
pub chunk: Chunk,
|
||||
pub sprite_bundle: SpriteBundle,
|
||||
}
|
||||
|
|
@ -19,30 +19,32 @@ pub struct Terrain2DPlugin;
|
|||
|
||||
impl Plugin for Terrain2DPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.register_type::<Chunk2DHandler>()
|
||||
app.register_type::<TerrainChunk2D>()
|
||||
.insert_resource(Terrain2D::new())
|
||||
.add_event::<TerrainEvent>()
|
||||
.add_system(emit_terrain_events)
|
||||
.add_system(chunk_spawner);
|
||||
.add_event::<TerrainEvent2D>()
|
||||
.add_system_to_stage(CoreStage::PostUpdate, emit_terrain_events)
|
||||
.add_system(chunk_spawner)
|
||||
.add_system(chunk_sprite_sync)
|
||||
.add_system(chunk_collision_sync);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_terrain_events(
|
||||
mut terrain: ResMut<Terrain2D>,
|
||||
mut terrain_events: EventWriter<TerrainEvent>,
|
||||
mut terrain_events: EventWriter<TerrainEvent2D>,
|
||||
) {
|
||||
for event in terrain.events.drain(..) {
|
||||
terrain_events.send(event)
|
||||
}
|
||||
for (chunk_index, mut chunk) in terrain.chunk_iter_mut() {
|
||||
for (chunk_index, chunk) in terrain.chunk_iter_mut() {
|
||||
if let Some(rect) = &chunk.dirty_rect {
|
||||
terrain_events.send(TerrainEvent::TexelsUpdated(*chunk_index, *rect));
|
||||
chunk.dirty_rect = None;
|
||||
terrain_events.send(TerrainEvent2D::TexelsUpdated(*chunk_index, *rect));
|
||||
chunk.mark_clean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TerrainEvent {
|
||||
pub enum TerrainEvent2D {
|
||||
ChunkAdded(Chunk2DIndex),
|
||||
ChunkRemoved(Chunk2DIndex),
|
||||
TexelsUpdated(Chunk2DIndex, ChunkRect),
|
||||
|
|
@ -51,7 +53,7 @@ pub enum TerrainEvent {
|
|||
#[derive(Default, Resource)]
|
||||
pub struct Terrain2D {
|
||||
chunk_map: HashMap<Chunk2DIndex, Chunk2D>,
|
||||
events: Vec<TerrainEvent>,
|
||||
events: Vec<TerrainEvent2D>,
|
||||
}
|
||||
|
||||
impl Terrain2D {
|
||||
|
|
@ -64,11 +66,11 @@ impl Terrain2D {
|
|||
|
||||
pub fn add_chunk(&mut self, index: Chunk2DIndex, chunk: Chunk2D) {
|
||||
self.chunk_map.insert(index, chunk);
|
||||
self.events.push(TerrainEvent::ChunkAdded(index))
|
||||
self.events.push(TerrainEvent2D::ChunkAdded(index))
|
||||
}
|
||||
|
||||
pub fn remove_chunk(&mut self, index: Chunk2DIndex) {
|
||||
self.events.push(TerrainEvent::ChunkRemoved(index));
|
||||
self.events.push(TerrainEvent2D::ChunkRemoved(index));
|
||||
self.chunk_map.remove(&index);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
use super::{
|
||||
local_to_texel_index, texel_index_to_local, Terrain2D, TerrainEvent, Texel2D, TexelID,
|
||||
local_to_texel_index, texel_index_to_local, Terrain2D, TerrainEvent2D, Texel2D, TexelID,
|
||||
NEIGHBOUR_INDEX_MAP,
|
||||
};
|
||||
use crate::util::{Segment2I, Vector2I};
|
||||
|
|
@ -64,14 +64,30 @@ lazy_static! {
|
|||
|
||||
#[derive(Reflect, Component, Default)]
|
||||
#[reflect(Component)]
|
||||
pub struct Chunk2DHandler {
|
||||
pub struct TerrainChunk2D {
|
||||
pub index: Chunk2DIndex,
|
||||
}
|
||||
|
||||
#[derive(Reflect, Component, Default)]
|
||||
#[reflect(Component)]
|
||||
pub struct TerrainChunkSpriteSync2D;
|
||||
|
||||
#[derive(Reflect, Component, Default)]
|
||||
#[reflect(Component)]
|
||||
pub struct TerrainChunkCollisionSync2D;
|
||||
|
||||
#[derive(Bundle, Default)]
|
||||
pub struct ChunkBundle {
|
||||
pub chunk: Chunk2DHandler,
|
||||
pub sprite_bundle: SpriteBundle,
|
||||
pub struct ChunkSpriteBundle {
|
||||
pub chunk: TerrainChunk2D,
|
||||
pub sync_flag: TerrainChunkSpriteSync2D,
|
||||
pub sprite: SpriteBundle,
|
||||
}
|
||||
|
||||
#[derive(Bundle, Default)]
|
||||
pub struct ChunkColliderBundle {
|
||||
pub chunk: TerrainChunk2D,
|
||||
pub sync_flag: TerrainChunkCollisionSync2D,
|
||||
pub transform: TransformBundle,
|
||||
}
|
||||
|
||||
pub type Chunk2DIndex = Vector2I;
|
||||
|
|
@ -191,6 +207,10 @@ impl Chunk2D {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mark_clean(&mut self) {
|
||||
self.dirty_rect = None;
|
||||
}
|
||||
|
||||
pub fn get_texel(&self, position: &Vector2I) -> Option<Texel2D> {
|
||||
local_to_texel_index(position).map(|i| self.texels[i])
|
||||
}
|
||||
|
|
@ -224,22 +244,8 @@ impl Chunk2D {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunk_spawner(
|
||||
mut commands: Commands,
|
||||
mut terrain_events: EventReader<TerrainEvent>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
terrain: Res<Terrain2D>,
|
||||
chunk_query: Query<(Entity, &Chunk2DHandler)>,
|
||||
) {
|
||||
for terrain_event in terrain_events.iter() {
|
||||
match terrain_event {
|
||||
TerrainEvent::ChunkAdded(chunk_index) => {
|
||||
let chunk = terrain.index_to_chunk(chunk_index).unwrap();
|
||||
|
||||
// Chunk sprite
|
||||
// TODO: Move to separate function
|
||||
pub fn create_texture_data(&self) -> Vec<u8> {
|
||||
let mut image_data = Vec::with_capacity(Chunk2D::SIZE_X * Chunk2D::SIZE_Y * 4);
|
||||
let fallback: [u8; 4] = [0x00, 0x00, 0x00, 0x00];
|
||||
for y in (0..Chunk2D::SIZE_Y).rev() {
|
||||
|
|
@ -247,7 +253,7 @@ pub fn chunk_spawner(
|
|||
image_data.append(
|
||||
&mut COLOR_MAP
|
||||
.get(
|
||||
&chunk
|
||||
&self
|
||||
.get_texel(&Vector2I::new(x as i32, y as i32))
|
||||
.unwrap()
|
||||
.id,
|
||||
|
|
@ -258,71 +264,12 @@ pub fn chunk_spawner(
|
|||
);
|
||||
}
|
||||
}
|
||||
let mut image = Image::new(
|
||||
Extent3d {
|
||||
width: Chunk2D::SIZE_X as u32,
|
||||
height: Chunk2D::SIZE_Y as u32,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
bevy::render::render_resource::TextureDimension::D2,
|
||||
image_data,
|
||||
bevy::render::render_resource::TextureFormat::Rgba8Unorm,
|
||||
);
|
||||
|
||||
image.sampler_descriptor = ImageSampler::nearest();
|
||||
let texture = images.add(image);
|
||||
|
||||
// Chunk collision
|
||||
// TODO: Move to separate function
|
||||
let collision_islands = generate_collision(chunk);
|
||||
|
||||
let pos = Vec2::from(*chunk_index * Chunk2D::SIZE);
|
||||
commands
|
||||
.spawn(ChunkBundle {
|
||||
chunk: Chunk2DHandler {
|
||||
index: *chunk_index,
|
||||
},
|
||||
sprite_bundle: SpriteBundle {
|
||||
sprite: Sprite {
|
||||
custom_size: Some(Vec2::from(Chunk2D::SIZE)),
|
||||
anchor: bevy::sprite::Anchor::BottomLeft,
|
||||
..default()
|
||||
},
|
||||
texture,
|
||||
transform: Transform::from_translation(Vec3::new(pos.x, pos.y, 0.0)),
|
||||
..default()
|
||||
},
|
||||
})
|
||||
.insert(Name::new(format!(
|
||||
"Chunk {},{}",
|
||||
chunk_index.x, chunk_index.y
|
||||
)))
|
||||
.with_children(|builder| {
|
||||
let mut index = 1;
|
||||
for island in collision_islands.iter() {
|
||||
builder
|
||||
.spawn(Collider::polyline(island.clone(), None))
|
||||
.insert(TransformBundle::default())
|
||||
.insert(Name::new(format!("Collision #{index}")));
|
||||
index += 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
TerrainEvent::ChunkRemoved(chunk_index) => {
|
||||
for (entity, chunk) in chunk_query.iter() {
|
||||
if chunk.index == *chunk_index {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
}
|
||||
}
|
||||
}
|
||||
TerrainEvent::TexelsUpdated(chunk_index, rect) => {}
|
||||
}
|
||||
}
|
||||
image_data
|
||||
}
|
||||
|
||||
pub fn generate_collision(chunk: &Chunk2D) -> Vec<Vec<Vec2>> {
|
||||
pub fn create_collision_data(&self) -> Vec<Vec<Vec2>> {
|
||||
let mut islands: Vec<Island> = Vec::new();
|
||||
for i in 0..chunk.texels.len() {
|
||||
for i in 0..self.texels.len() {
|
||||
let local = texel_index_to_local(i);
|
||||
|
||||
let edge_mask: u8 = if local.y == Chunk2D::SIZE.y - 1 {
|
||||
|
|
@ -337,8 +284,8 @@ pub fn generate_collision(chunk: &Chunk2D) -> Vec<Vec<Vec2>> {
|
|||
| if local.x == 0 { 1 << 3 } else { 0 };
|
||||
|
||||
let mut sides: Vec<Segment2I>;
|
||||
if chunk.texels[i].is_empty() {
|
||||
sides = MST_CASE_MAP[chunk.texels[i].neighbour_mask as usize]
|
||||
if self.texels[i].is_empty() {
|
||||
sides = MST_CASE_MAP[self.texels[i].neighbour_mask as usize]
|
||||
.iter()
|
||||
.clone()
|
||||
.map(|side| Segment2I {
|
||||
|
|
@ -346,7 +293,7 @@ pub fn generate_collision(chunk: &Chunk2D) -> Vec<Vec<Vec2>> {
|
|||
to: side.to + local,
|
||||
})
|
||||
.collect();
|
||||
} else if !chunk.texels[i].is_empty() && edge_mask != 0 {
|
||||
} else if !self.texels[i].is_empty() && edge_mask != 0 {
|
||||
sides = Vec::with_capacity(Chunk2D::SIZE_X * 2 + Chunk2D::SIZE_Y * 2);
|
||||
for i in 0..MST_EDGE_CASE_MAP.len() {
|
||||
if edge_mask & (1 << i) != 0 {
|
||||
|
|
@ -449,3 +396,214 @@ pub fn generate_collision(chunk: &Chunk2D) -> Vec<Vec<Vec2>> {
|
|||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunk_spawner(
|
||||
mut commands: Commands,
|
||||
mut terrain_events: EventReader<TerrainEvent2D>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
chunk_query: Query<(Entity, &TerrainChunk2D)>,
|
||||
) {
|
||||
for terrain_event in terrain_events.iter() {
|
||||
match terrain_event {
|
||||
TerrainEvent2D::ChunkAdded(chunk_index) => {
|
||||
// Create unique handle for the image
|
||||
// TODO: recycling image data would be nice
|
||||
let mut image = Image::new(
|
||||
Extent3d {
|
||||
width: Chunk2D::SIZE_X as u32,
|
||||
height: Chunk2D::SIZE_Y as u32,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
bevy::render::render_resource::TextureDimension::D2,
|
||||
vec![0x00; Chunk2D::SIZE_X * Chunk2D::SIZE_Y * 4],
|
||||
bevy::render::render_resource::TextureFormat::Rgba8Unorm,
|
||||
);
|
||||
image.sampler_descriptor = ImageSampler::nearest();
|
||||
let texture = images.add(image);
|
||||
|
||||
let pos = Vec2::from(*chunk_index * Chunk2D::SIZE);
|
||||
commands
|
||||
.spawn(ChunkSpriteBundle {
|
||||
chunk: TerrainChunk2D {
|
||||
index: *chunk_index,
|
||||
},
|
||||
sprite: SpriteBundle {
|
||||
sprite: Sprite {
|
||||
custom_size: Some(Vec2::from(Chunk2D::SIZE)),
|
||||
anchor: bevy::sprite::Anchor::BottomLeft,
|
||||
..default()
|
||||
},
|
||||
texture,
|
||||
transform: Transform::from_translation(Vec3::new(pos.x, pos.y, 0.0)),
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
})
|
||||
.insert(Name::new(format!(
|
||||
"Chunk Sprite {},{}",
|
||||
chunk_index.x, chunk_index.y
|
||||
)));
|
||||
|
||||
commands
|
||||
.spawn(ChunkColliderBundle {
|
||||
chunk: TerrainChunk2D {
|
||||
index: *chunk_index,
|
||||
},
|
||||
transform: TransformBundle::from_transform(Transform::from_translation(
|
||||
Vec3::new(pos.x, pos.y, 0.0),
|
||||
)),
|
||||
..default()
|
||||
})
|
||||
.insert(Name::new(format!(
|
||||
"Chunk Collider {},{}",
|
||||
chunk_index.x, chunk_index.y
|
||||
)));
|
||||
}
|
||||
TerrainEvent2D::ChunkRemoved(chunk_index) => {
|
||||
for (entity, chunk) in chunk_query.iter() {
|
||||
if chunk.index == *chunk_index {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Update the chunk sprite as needed
|
||||
*/
|
||||
pub fn chunk_sprite_sync(
|
||||
mut terrain_events: EventReader<TerrainEvent2D>,
|
||||
mut commands: Commands,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
mut sprite_query: Query<&mut Sprite>,
|
||||
terrain: Res<Terrain2D>,
|
||||
added_chunk_query: Query<
|
||||
(Entity, &TerrainChunk2D),
|
||||
(With<TerrainChunkSpriteSync2D>, Changed<TerrainChunk2D>),
|
||||
>,
|
||||
chunk_query: Query<(Entity, &TerrainChunk2D), (With<TerrainChunkSpriteSync2D>, With<Sprite>)>,
|
||||
texture_query: Query<&Handle<Image>>,
|
||||
) {
|
||||
let mut updated_chunks: Vec<(Entity, &TerrainChunk2D, Option<ChunkRect>)> = vec![];
|
||||
|
||||
// Check for added components
|
||||
for (added_entity, added_chunk) in added_chunk_query.iter() {
|
||||
updated_chunks.push((added_entity, added_chunk, None));
|
||||
}
|
||||
|
||||
// Check for terrain events
|
||||
for event in terrain_events.iter() {
|
||||
for (entity, chunk) in chunk_query.iter() {
|
||||
let (chunk_index, rect) = match event {
|
||||
TerrainEvent2D::ChunkAdded(chunk_index) => {
|
||||
// The entity should not have the time to react to the event since it was just made
|
||||
println!("[chunk_sprite_sync -> TerrainEvent2D::ChunkAdded] This probably shouldn't be firing, maybe the chunk was destroyed and immediately created? chunk: {chunk_index:?}");
|
||||
(chunk_index, None)
|
||||
}
|
||||
TerrainEvent2D::TexelsUpdated(chunk_index, rect) => (chunk_index, Some(*rect)),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
if *chunk_index != chunk.index {
|
||||
continue;
|
||||
};
|
||||
|
||||
updated_chunks.push((entity, chunk, rect));
|
||||
}
|
||||
}
|
||||
|
||||
// Update sprite
|
||||
for (entity, chunk, rect) in updated_chunks {
|
||||
let chunk = terrain.index_to_chunk(&chunk.index).unwrap();
|
||||
let rect = rect.unwrap_or(ChunkRect {
|
||||
min: Vector2I::ZERO,
|
||||
max: Chunk2D::SIZE - Vector2I::ONE,
|
||||
});
|
||||
|
||||
let mut sprite = match sprite_query.get_mut(entity) {
|
||||
Ok(sprite) => sprite,
|
||||
Err(err) => {
|
||||
println!("[chunk_sprite_sync] Sprite component not found for entity:");
|
||||
commands.entity(entity).log_components();
|
||||
println!("{err:?}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let handle = texture_query.get(entity).unwrap();
|
||||
let mut image = images.get_mut(handle).unwrap();
|
||||
let image_data = chunk.create_texture_data();
|
||||
image.data = image_data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Create and update colliders for chunk as needed
|
||||
*/
|
||||
pub fn chunk_collision_sync(
|
||||
mut terrain_events: EventReader<TerrainEvent2D>,
|
||||
mut commands: Commands,
|
||||
terrain: Res<Terrain2D>,
|
||||
added_chunk_query: Query<
|
||||
(Entity, &TerrainChunk2D),
|
||||
(With<TerrainChunkCollisionSync2D>, Changed<TerrainChunk2D>),
|
||||
>,
|
||||
chunk_query: Query<(Entity, &TerrainChunk2D), With<TerrainChunkCollisionSync2D>>,
|
||||
child_collider_query: Query<&Children, With<Collider>>,
|
||||
) {
|
||||
let mut updated_chunks = vec![];
|
||||
|
||||
// Check for added components
|
||||
for (added_entity, added_chunk) in added_chunk_query.iter() {
|
||||
updated_chunks.push((added_entity, added_chunk));
|
||||
}
|
||||
|
||||
// Check for terrain events
|
||||
for event in terrain_events.iter() {
|
||||
for (entity, chunk) in chunk_query.iter() {
|
||||
let chunk_index = match event {
|
||||
TerrainEvent2D::ChunkAdded(chunk_index) => {
|
||||
// The entity should not have the time to react to the event since it was just made
|
||||
println!("[chunk_collision_sync -> TerrainEvent2D::ChunkAdded] This probably shouldn't be firing, maybe the chunk was destroyed and immediately created? chunk: {chunk_index:?}");
|
||||
chunk_index
|
||||
}
|
||||
TerrainEvent2D::TexelsUpdated(chunk_index, _) => chunk_index,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
if *chunk_index != chunk.index {
|
||||
continue;
|
||||
};
|
||||
|
||||
updated_chunks.push((entity, chunk));
|
||||
}
|
||||
}
|
||||
|
||||
for (entity, chunk) in updated_chunks.iter() {
|
||||
// Remove old colliders
|
||||
for children in child_collider_query.get(*entity) {
|
||||
for child in children {
|
||||
commands.entity(*child).despawn_recursive()
|
||||
}
|
||||
}
|
||||
|
||||
let chunk = terrain.index_to_chunk(&chunk.index).unwrap();
|
||||
|
||||
// Add new colliders
|
||||
let collision_islands = chunk.create_collision_data();
|
||||
commands.entity(*entity).with_children(|builder| {
|
||||
let mut index = 1;
|
||||
for island in collision_islands.iter() {
|
||||
builder
|
||||
.spawn(Collider::polyline(island.clone(), None))
|
||||
.insert(TransformBundle::default())
|
||||
.insert(Name::new(format!("Island #{index}")));
|
||||
index += 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue