TilemapChunk cleanup (#19795)

# Objective

- Make follow-up changes from #18866 

## Solution

  - Switch from the on add observer to an on insert hook
  - Make the component immutable
  - Remove required components

## Testing

- `tilemap_chunk` example
This commit is contained in:
Conner Petzold 2025-06-24 18:25:59 -04:00 committed by GitHub
parent a3d406dd49
commit 04c2b32be8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 44 deletions

View File

@ -5,11 +5,11 @@ use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{ use bevy_ecs::{
component::Component, component::Component,
entity::Entity, entity::Entity,
lifecycle::Add, lifecycle::HookContext,
observer::On,
query::Changed, query::Changed,
resource::Resource, resource::Resource,
system::{Commands, Query, ResMut}, system::{Query, ResMut},
world::DeferredWorld,
}; };
use bevy_image::{Image, ImageSampler}; use bevy_image::{Image, ImageSampler};
use bevy_math::{FloatOrd, UVec2, Vec2, Vec3}; use bevy_math::{FloatOrd, UVec2, Vec2, Vec3};
@ -33,7 +33,6 @@ pub struct TilemapChunkPlugin;
impl Plugin for TilemapChunkPlugin { impl Plugin for TilemapChunkPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<TilemapChunkMeshCache>() app.init_resource::<TilemapChunkMeshCache>()
.add_observer(on_add_tilemap_chunk)
.add_systems(Update, update_tilemap_chunk_indices); .add_systems(Update, update_tilemap_chunk_indices);
} }
} }
@ -47,7 +46,8 @@ pub struct TilemapChunkMeshCache(HashMap<TilemapChunkMeshCacheKey, Handle<Mesh>>
/// A component representing a chunk of a tilemap. /// A component representing a chunk of a tilemap.
/// Each chunk is a rectangular section of tiles that is rendered as a single mesh. /// Each chunk is a rectangular section of tiles that is rendered as a single mesh.
#[derive(Component, Clone, Debug, Default)] #[derive(Component, Clone, Debug, Default)]
#[require(Mesh2d, MeshMaterial2d<TilemapChunkMaterial>, TilemapChunkIndices, Anchor)] #[require(Anchor)]
#[component(immutable, on_insert = on_insert_tilemap_chunk)]
pub struct TilemapChunk { pub struct TilemapChunk {
/// The size of the chunk in tiles /// The size of the chunk in tiles
pub chunk_size: UVec2, pub chunk_size: UVec2,
@ -62,31 +62,26 @@ pub struct TilemapChunk {
/// Component storing the indices of tiles within a chunk. /// Component storing the indices of tiles within a chunk.
/// Each index corresponds to a specific tile in the tileset. /// Each index corresponds to a specific tile in the tileset.
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)] #[derive(Component, Clone, Debug, Deref, DerefMut)]
pub struct TilemapChunkIndices(pub Vec<Option<u16>>); pub struct TilemapChunkIndices(pub Vec<Option<u16>>);
fn on_add_tilemap_chunk( fn on_insert_tilemap_chunk(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
trigger: On<Add, TilemapChunk>, let Some(tilemap_chunk) = world.get::<TilemapChunk>(entity) else {
tilemap_chunk_query: Query<(&TilemapChunk, &TilemapChunkIndices, &Anchor)>, warn!("TilemapChunk not found for tilemap chunk {}", entity);
mut commands: Commands, return;
mut meshes: ResMut<Assets<Mesh>>, };
mut materials: ResMut<Assets<TilemapChunkMaterial>>,
mut images: ResMut<Assets<Image>>, let chunk_size = tilemap_chunk.chunk_size;
mut tilemap_chunk_mesh_cache: ResMut<TilemapChunkMeshCache>, let alpha_mode = tilemap_chunk.alpha_mode;
) { let tileset = tilemap_chunk.tileset.clone();
let chunk_entity = trigger.target();
let Ok(( let Some(indices) = world.get::<TilemapChunkIndices>(entity) else {
TilemapChunk { warn!("TilemapChunkIndices not found for tilemap chunk {}", entity);
chunk_size, return;
tile_display_size, };
tileset,
alpha_mode, let Some(&anchor) = world.get::<Anchor>(entity) else {
}, warn!("Anchor not found for tilemap chunk {}", entity);
indices,
anchor,
)) = tilemap_chunk_query.get(chunk_entity)
else {
warn!("Tilemap chunk {} not found", chunk_entity);
return; return;
}; };
@ -94,7 +89,7 @@ fn on_add_tilemap_chunk(
if indices.len() != expected_indices_length { if indices.len() != expected_indices_length {
warn!( warn!(
"Invalid indices length for tilemap chunk {} of size {}. Expected {}, got {}", "Invalid indices length for tilemap chunk {} of size {}. Expected {}, got {}",
chunk_entity, entity,
chunk_size, chunk_size,
indices.len(), indices.len(),
expected_indices_length expected_indices_length
@ -102,30 +97,40 @@ fn on_add_tilemap_chunk(
return; return;
} }
let indices_image = make_chunk_image(chunk_size, &indices.0); let indices_image = make_chunk_image(&chunk_size, &indices.0);
let display_size = (chunk_size * tile_display_size).as_vec2(); let display_size = (chunk_size * tilemap_chunk.tile_display_size).as_vec2();
let mesh_key: TilemapChunkMeshCacheKey = ( let mesh_key: TilemapChunkMeshCacheKey = (
*chunk_size, chunk_size,
FloatOrd(display_size.x), FloatOrd(display_size.x),
FloatOrd(display_size.y), FloatOrd(display_size.y),
FloatOrd(anchor.as_vec().x), FloatOrd(anchor.as_vec().x),
FloatOrd(anchor.as_vec().y), FloatOrd(anchor.as_vec().y),
); );
let mesh = tilemap_chunk_mesh_cache let tilemap_chunk_mesh_cache = world.resource::<TilemapChunkMeshCache>();
.entry(mesh_key) let mesh = if let Some(mesh) = tilemap_chunk_mesh_cache.get(&mesh_key) {
.or_insert_with(|| meshes.add(make_chunk_mesh(chunk_size, &display_size, anchor))); mesh.clone()
} else {
let mut meshes = world.resource_mut::<Assets<Mesh>>();
meshes.add(make_chunk_mesh(&chunk_size, &display_size, &anchor))
};
commands.entity(chunk_entity).insert(( let mut images = world.resource_mut::<Assets<Image>>();
Mesh2d(mesh.clone()), let indices = images.add(indices_image);
MeshMaterial2d(materials.add(TilemapChunkMaterial {
tileset: tileset.clone(), let mut materials = world.resource_mut::<Assets<TilemapChunkMaterial>>();
indices: images.add(indices_image), let material = materials.add(TilemapChunkMaterial {
alpha_mode: *alpha_mode, tileset,
})), indices,
)); alpha_mode,
});
world
.commands()
.entity(entity)
.insert((Mesh2d(mesh), MeshMaterial2d(material)));
} }
fn update_tilemap_chunk_indices( fn update_tilemap_chunk_indices(
@ -154,6 +159,7 @@ fn update_tilemap_chunk_indices(
continue; continue;
} }
// Getting the material mutably to trigger change detection
let Some(material) = materials.get_mut(material.id()) else { let Some(material) = materials.get_mut(material.id()) else {
warn!( warn!(
"TilemapChunkMaterial not found for tilemap chunk {}", "TilemapChunkMaterial not found for tilemap chunk {}",

View File

@ -9,7 +9,6 @@ use bevy_render::{
}; };
/// Plugin that adds support for tilemap chunk materials. /// Plugin that adds support for tilemap chunk materials.
#[derive(Default)]
pub struct TilemapChunkMaterialPlugin; pub struct TilemapChunkMaterialPlugin;
impl Plugin for TilemapChunkMaterialPlugin { impl Plugin for TilemapChunkMaterialPlugin {