Skip to content

Commit 49a4737

Browse files
Removal wip
1 parent f659c85 commit 49a4737

File tree

5 files changed

+144
-20
lines changed

5 files changed

+144
-20
lines changed

crates/bevy_sprite/src/tilemap/commands.rs

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1+
use std::marker::PhantomData;
2+
13
use crate::tilemap::{TileData, TileStorage, Tilemap};
2-
use bevy_ecs::{entity::Entity, error::HandleError, hierarchy::ChildOf, system::{Command, Commands}, world::World};
3-
use bevy_math::{IVec2, UVec2, Vec2, Vec3};
4+
use bevy_ecs::{entity::Entity, hierarchy::ChildOf, system::{Command, Commands}, world::World};
5+
use bevy_math::{IVec2, Vec2, Vec3};
46
use bevy_transform::components::Transform;
57

68
pub trait CommandsTilemapExt {
79
fn set_tile<T: TileData>(
810
&mut self,
9-
tilemap: Entity,
11+
tilemap_id: Entity,
1012
tile_position: IVec2,
1113
maybe_tile: Option<T>,
1214
);
1315

14-
fn remove_tile(&mut self, tilemap: Entity, tile_position: IVec2);
16+
fn remove_tile<T: TileData>(&mut self, tilemap_id: Entity, tile_position: IVec2);
1517
}
1618

1719
impl CommandsTilemapExt for Commands<'_, '_> {
@@ -24,8 +26,8 @@ impl CommandsTilemapExt for Commands<'_, '_> {
2426
self.queue(move |world: &mut World| {SetTile {tilemap_id, tile_position, maybe_tile }.apply(world);});
2527
}
2628

27-
fn remove_tile(&mut self, tilemap: Entity, tile_position: IVec2) {
28-
todo!()
29+
fn remove_tile<T: TileData>(&mut self, tilemap_id: Entity, tile_position: IVec2) {
30+
self.queue(move |world: &mut World| {RemoveTile::<T> {tilemap_id, tile_position, _t: PhantomData::default() }.apply(world);});
2931
}
3032
}
3133

@@ -59,7 +61,7 @@ impl<T: TileData> Command<SetTileResult<T>> for SetTile<T> {
5961
};
6062

6163
let chunk_position = tilemap.tile_chunk_position(self.tile_position);
62-
let tile_position = tilemap.tile_chunk_local_position(self.tile_position);
64+
let tile_relative_position = tilemap.tile_relative_position(self.tile_position);
6365

6466
if let Some(tile_storage_id) = tilemap.chunks.get(&chunk_position).cloned() {
6567
let replaced_tile = tilemap_entity.world_scope(move |w| {
@@ -77,15 +79,15 @@ impl<T: TileData> Command<SetTileResult<T>> for SetTile<T> {
7779
return None;
7880
};
7981

80-
tile_storage.set(tile_position, self.maybe_tile)
82+
tile_storage.set(tile_relative_position, self.maybe_tile)
8183
});
8284
SetTileResult { chunk_id: Some(tile_storage_id), replaced_tile }
8385
} else {
8486
let chunk_size = tilemap.chunk_size;
8587
let tile_size = tilemap.tile_display_size;
8688
let tile_storage_id = tilemap_entity.world_scope(move |w| {
8789
let mut tile_storage = TileStorage::<T>::new(chunk_size);
88-
tile_storage.set(tile_position, self.maybe_tile);
90+
tile_storage.set(tile_relative_position, self.maybe_tile);
8991
let translation = Vec2::new(chunk_size.x as f32, chunk_size.y as f32) * Vec2::new(tile_size.x as f32, tile_size.y as f32) * Vec2::new(chunk_position.x as f32, chunk_position.y as f32);
9092
let translation = Vec3::new(translation.x, translation.y, 0.0);
9193
let transform = Transform::from_translation(translation);
@@ -99,4 +101,50 @@ impl<T: TileData> Command<SetTileResult<T>> for SetTile<T> {
99101
SetTileResult { chunk_id: Some(tile_storage_id), replaced_tile: None }
100102
}
101103
}
104+
}
105+
106+
pub struct RemoveTile<T: TileData> {
107+
pub tilemap_id: Entity,
108+
pub tile_position: IVec2,
109+
pub _t: PhantomData<T>
110+
}
111+
112+
impl<T: TileData> Command<Option<T>> for RemoveTile<T> {
113+
fn apply(self, world: &mut World) -> Option<T> {
114+
let Ok(mut tilemap_entity) = world.get_entity_mut(self.tilemap_id) else {
115+
tracing::warn!("Could not find Tilemap Entity {:?}", self.tilemap_id);
116+
return Default::default();
117+
};
118+
119+
let Some(tilemap) = tilemap_entity.get::<Tilemap>() else {
120+
tracing::warn!("Could not find Tilemap on Entity {:?}", self.tilemap_id);
121+
return Default::default();
122+
};
123+
124+
let chunk_position = tilemap.tile_chunk_position(self.tile_position);
125+
let tile_relative_position = tilemap.tile_relative_position(self.tile_position);
126+
127+
if let Some(tile_storage_id) = tilemap.chunks.get(&chunk_position).cloned() {
128+
tilemap_entity.world_scope(move |w| {
129+
let Ok(mut tilestorage_entity) = w.get_entity_mut(tile_storage_id) else {
130+
tracing::warn!("Could not find TileStorage Entity {:?}", tile_storage_id);
131+
return None;
132+
};
133+
134+
let Some(mut tile_storage) = tilestorage_entity.get_mut::<TileStorage<T>>()
135+
else {
136+
tracing::warn!(
137+
"Could not find TileStorage on Entity {:?}",
138+
tile_storage_id
139+
);
140+
return None;
141+
};
142+
143+
tile_storage.remove(tile_relative_position)
144+
})
145+
}
146+
else {
147+
None
148+
}
149+
}
102150
}

crates/bevy_sprite/src/tilemap/entity_tiles.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1+
use std::marker::PhantomData;
2+
13
use bevy_app::{App, Plugin};
24
use bevy_derive::Deref;
35
use bevy_ecs::{component::Component, entity::Entity, hierarchy::ChildOf, lifecycle::HookContext, system::Command, world::{DeferredWorld, World}};
46
use bevy_math::IVec2;
57
use tracing::warn;
68

7-
use crate::{SetTile, SetTileResult, TileData};
9+
use crate::{RemoveTile, SetTile, SetTileResult, TileData};
810

911
/// Plugin that handles the initialization and updating of tilemap chunks.
1012
/// Adds systems for processing newly added tilemap chunks.
1113
pub struct EntityTilePlugin;
1214

1315
impl Plugin for EntityTilePlugin {
1416
fn build(&self, app: &mut App) {
15-
app.world_mut().register_component_hooks::<TileCoord>().on_insert(on_insert_entity_tile);
17+
app.world_mut().register_component_hooks::<TileCoord>().on_insert(on_insert_entity_tile).on_remove(on_remove_entity_tile);
18+
app.world_mut().register_component_hooks::<InMap>().on_remove(on_remove_entity_tile);
1619
}
1720
}
1821

@@ -24,11 +27,16 @@ impl TileData for EntityTile {
2427
}
2528

2629
#[derive(Component, Clone, Debug, Deref)]
30+
#[component(immutable)]
2731
pub struct InMap(pub Entity);
2832

2933
#[derive(Component, Clone, Debug, Deref)]
34+
#[component(immutable)]
3035
pub struct TileCoord(pub IVec2);
3136

37+
#[derive(Component, Clone, Debug)]
38+
pub struct DespawnOnRemove;
39+
3240
fn on_insert_entity_tile(mut world: DeferredWorld, HookContext { entity, .. }: HookContext){
3341
let Ok(tile) = world.get_entity(entity) else {
3442
warn!("Tile {} not found", entity);
@@ -58,7 +66,47 @@ fn on_insert_entity_tile(mut world: DeferredWorld, HookContext { entity, .. }: H
5866
world.entity_mut(entity).insert(ChildOf(chunk_id));
5967

6068
if let Some(replaced_tile) = replaced_tile {
61-
world.despawn(replaced_tile.0);
69+
let mut replaced_tile = world.entity_mut(replaced_tile.0);
70+
if replaced_tile.contains::<DespawnOnRemove>() {
71+
replaced_tile.despawn();
72+
} else {
73+
replaced_tile.remove::<(InMap, TileCoord)>();
74+
}
75+
}
76+
});
77+
}
78+
79+
fn on_remove_entity_tile(mut world: DeferredWorld, HookContext { entity, .. }: HookContext){
80+
let Ok(tile) = world.get_entity(entity) else {
81+
warn!("Tile {} not found", entity);
82+
return;
83+
};
84+
let Some(in_map) = tile.get::<InMap>().cloned() else {
85+
warn!("Tile {} is not in a TileMap", entity);
86+
return;
87+
};
88+
let Some(tile_position) = tile.get::<TileCoord>().cloned() else {
89+
warn!("Tile {} has no tile coord.", entity);
90+
return;
91+
};
92+
93+
world
94+
.commands()
95+
.queue(move |world: &mut World| {
96+
let Some(removed) = RemoveTile::<EntityTile> {
97+
tilemap_id: in_map.0,
98+
tile_position: tile_position.0,
99+
_t: PhantomData,
100+
}.apply(world) else {
101+
warn!("Tile {} could not be removed from map or was already removed.", entity);
102+
return;
103+
};
104+
105+
let mut removed = world.entity_mut(removed.0);
106+
if removed.contains::<DespawnOnRemove>() {
107+
removed.despawn();
108+
} else {
109+
removed.remove::<InMap>();
62110
}
63111
});
64112
}

crates/bevy_sprite/src/tilemap/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ impl Tilemap {
5252
}
5353

5454
/// Get the coordinates with in a chunk from a tiles global coordinates.
55-
// TODO: NAME THIS BETTER
56-
pub fn tile_chunk_local_position(&self, tile_position: IVec2) -> UVec2 {
55+
pub fn tile_relative_position(&self, tile_position: IVec2) -> UVec2 {
5756
let chunk_size = self
5857
.chunk_size
5958
.try_into()

crates/bevy_sprite_render/src/tilemap_chunk/mod.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::any::type_name;
1+
use std::{any::type_name, marker::PhantomData};
22

33
use crate::{AlphaMode2d, MeshMaterial2d};
44
use bevy_app::{App, Plugin, Update};
@@ -14,7 +14,7 @@ use bevy_math::{primitives::Rectangle, UVec2};
1414
use bevy_mesh::{Mesh, Mesh2d};
1515
use bevy_platform::collections::HashMap;
1616
use bevy_reflect::{prelude::*, Reflect};
17-
use bevy_sprite::{InMap, SetTile, TileCoord, TileData, TileStorage, Tilemap};
17+
use bevy_sprite::{InMap, RemoveTile, SetTile, TileCoord, TileData, TileStorage, Tilemap};
1818
use bevy_transform::components::Transform;
1919
use bevy_utils::default;
2020
use tracing::{trace, warn};
@@ -32,7 +32,7 @@ impl Plugin for TilemapChunkPlugin {
3232
app.init_resource::<TilemapChunkMeshCache>()
3333
.add_systems(Update, update_tilemap_chunk_indices);
3434
app.world_mut().register_component_hooks::<TileStorage<TileRenderData>>().on_insert(on_insert_chunk_tile_render_data);
35-
app.world_mut().register_component_hooks::<TileRenderData>().on_insert(on_insert_tile_render_data);
35+
app.world_mut().register_component_hooks::<TileRenderData>().on_insert(on_insert_tile_render_data).on_remove(on_remove_tile_render_data);
3636
}
3737
}
3838

@@ -139,6 +139,7 @@ fn on_insert_chunk_tile_render_data(mut world: DeferredWorld, HookContext { enti
139139
/// Data for a single tile in the tilemap chunk.
140140
#[derive(Component, Clone, Copy, Debug, Reflect)]
141141
#[reflect(Clone, Debug, Default)]
142+
#[component(immutable)]
142143
pub struct TileRenderData {
143144
/// The index of the tile in the corresponding tileset array texture.
144145
pub tileset_index: u16,
@@ -293,7 +294,6 @@ fn update_tilemap_chunk_indices(
293294
}
294295
}
295296

296-
297297
fn on_insert_tile_render_data(mut world: DeferredWorld, HookContext { entity, .. }: HookContext){
298298
let Ok(tile) = world.get_entity(entity) else {
299299
warn!("Tile {} not found", entity);
@@ -321,4 +321,33 @@ fn on_insert_tile_render_data(mut world: DeferredWorld, HookContext { entity, ..
321321
maybe_tile: Some(tile_render_data),
322322
}.apply(world);
323323
});
324+
}
325+
326+
fn on_remove_tile_render_data(mut world: DeferredWorld, HookContext { entity, .. }: HookContext){
327+
let Ok(tile) = world.get_entity(entity) else {
328+
warn!("Tile {} not found", entity);
329+
return;
330+
};
331+
let Some(in_map) = tile.get::<InMap>().cloned() else {
332+
warn!("Tile {} is not in a TileMap", entity);
333+
return;
334+
};
335+
let Some(tile_position) = tile.get::<TileCoord>().cloned() else {
336+
warn!("Tile {} has no tile coord.", entity);
337+
return;
338+
};
339+
let Some(tile_render_data) = tile.get::<TileRenderData>().cloned() else {
340+
warn!("Tile {} does not have TileRenderData", entity);
341+
return;
342+
};
343+
344+
world
345+
.commands()
346+
.queue(move |world: &mut World| {
347+
RemoveTile::<TileRenderData> {
348+
tilemap_id: in_map.0,
349+
tile_position: tile_position.0,
350+
_t: PhantomData::default(),
351+
}.apply(world);
352+
});
324353
}

examples/2d/tilemap_entities.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Shows a tilemap chunk rendered with a single draw call.
22
33
use bevy::{
4-
color::palettes::tailwind::RED_400, image::{ImageArrayLayout, ImageLoaderSettings}, prelude::*, sprite::{CommandsTilemapExt, InMap, TileCoord, TileStorage, Tilemap}, sprite_render::{TileRenderData, TilemapChunkRenderData, TilemapRenderData}
4+
color::palettes::tailwind::RED_400, image::{ImageArrayLayout, ImageLoaderSettings}, prelude::*, sprite::{CommandsTilemapExt, DespawnOnRemove, InMap, TileCoord, TileStorage, Tilemap}, sprite_render::{TileRenderData, TilemapChunkRenderData, TilemapRenderData}
55
};
66
use rand::{Rng, SeedableRng};
77
use rand_chacha::ChaCha8Rng;
@@ -60,7 +60,7 @@ fn update_tilemap(
6060
let x = rng.random_range(-64..=64);
6161
let y = rng.random_range(-64..=64);
6262

63-
commands.spawn((InMap(map), TileCoord(IVec2::new(x, y)), TileRenderData { tileset_index: rng.random_range(0..4), ..Default::default()}));
63+
commands.spawn((InMap(map), TileCoord(IVec2::new(x, y)), TileRenderData { tileset_index: rng.random_range(0..4), ..Default::default()}, DespawnOnRemove));
6464
}
6565
}
6666

0 commit comments

Comments
 (0)