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

View File

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