Skip to content

Conversation

@jamescarterbell
Copy link
Contributor

@jamescarterbell jamescarterbell commented Nov 5, 2025

Objective

  • Add Tilemap support to Bevy.
  • Support entities as tiles.
  • Support non-entity tile workflows and raw tile storage access for libraries.
  • Support ergonomic/bevy-styled tile index lookups and queries for all tile types.
  • Support tile iteration in a region.

Non-Objectives

  • Supporting non-square tile shapes.
  • Supporting 3d tiles (extending this wouldn't be difficult though).

Existing Solutions

Currently Bevy has the TilemapChunkRender component to render single tilemap chunks, but no built in way to support auto chunking or actual tilemaps. Engines like Godot and Unity have native full tilemap support with no preset bounds and automatic chunking, this seems like a good feature for Bevy.

bevy_ecs_tilemap exists and supports entities as tiles as well as many tile shapes, it's also very popular. However, I don't think simply porting it into current bevy is the right approach for three reasons:

  1. It doesn't support autochunking, this is an important feature for ease of use.
  2. It forces tiles to be entities, I think this should be a choice made for the user. For mostly static tilemaps, many may not opt in to this behavior.
  3. Accessing tile data spatially is not ergonomic, requiring users to look up the entity in the tilestorage, and then manually pass it into whatever query they are using, when it could be a one function look up.

Solution

Tilemap and TileStorage

Tilemaps are a component that go on a top level entity and map chunk coordinates to chunk entities as well as the chunk size and tile size.

Chunks contain TileStorage<T> components that store typed tile data as a vec, as well as a TileStorages component that stores untyped information for adding and removing tiles to a TileStorage<T> (This is necessary for automatically removing all tile info for a given tile when it's removed from the map).

If a user wants to spawn a tile entity, they simply need to spawn an entity with (InMap(tilemap_entity_here), TileCoord::new(x, y). A component hook will then handle the following:

  1. Try to get the tilemap.
  2. From the tilemap, look up the chunk using the TileCoord, if the chunk doesn't exist, spawn it.
  3. On the chunk, try to get the component TileStorage<EntityTile>, if it doesn't exist, insert it into the chunk.
  4. Add the tiles entity to TileStorage<EntityTile> by calculating the index from TileCoord.

If a user wants to query a tile entity, they can use TilemapEntityQuery<D, F>:

fn on_bomb_hit(
    hit: On<BrickHit>,
    map: Single<Entity, With<Tilemap>>,
    bombs: Query<(Entity, &TileCoord), With<Bomb>>,
    bricks: TilemapEntityQuery<Entity, With<Brick>>,
    mut score: ResMut<Score>,
    mut commands: Commands,
) {
    let Ok((bomb_id, bomb_coord)) = bombs.get(hit.0) else {
        return;
    };
    commands.entity(bomb_id).despawn();
    **score += 1;

    let Some(bricks) = bricks.get_map(*map) else {
        return;
    };

    for coord in bomb_coord.adjacent() {
        // Look up is a one step process, similar to a normal query get, just with a coordinate instead.
        if let Some(brick) = bricks.get_at(*coord) {
            commands.entity(brick).trigger(BrickHit);
        }
    }
}

Testing

TODO


Showcase

examples/2d/tilemap.rs

tilemap.mp4

Uses non-entity tiles to spawn tiles on click.

examples/2d/tilemap_entites.rs

tilemap_entities.mp4

Spawns entity tiles randomly, rotating the tilemap to show transforms working.

examples/games/breakout.rs

breakout.mp4

Used tilemaps to add bomb tiles that take out surrounding tiles.

TODO

TODO

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-Rendering Drawing game state to the screen S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Nov 7, 2025
@github-actions
Copy link
Contributor

The generated examples/README.md is out of sync with the example metadata in Cargo.toml or the example readme template. Please run cargo run -p build-templated-pages -- update examples to update it, and commit the file change.

@jamescarterbell jamescarterbell force-pushed the chunk_storage branch 2 times, most recently from b64736f to e48a274 Compare December 23, 2025 20:15
@jamescarterbell jamescarterbell changed the title Chunk storage Tilemaps Dec 28, 2025
@ChristopherBiscardi
Copy link
Contributor

bevy_ecs_tilemap exists and supports entities as tiles as well as many tile shapes, it's also very popular. However, I don't think simply porting it into current bevy is the right approach for three reasons:

  1. It doesn't support autochunking, this is an important feature for ease of use.
  2. It forces tiles to be entities, I think this should be a choice made for the user. For mostly static tilemaps, many may not opt in to this behavior.
  3. Accessing tile data spatially is not ergonomic, requiring users to look up the entity in the tilestorage, and then manually pass it into whatever query they are using, when it could be a one function look up.

bevy_ecs_tilemap comes up in every conversation on the topic so some corrections:

"simply porting it into bevy" was never an option. My original intention was to evolve the crate to fit upstream's needs but that was not a path upstream wanted to take. Previously raised issues like Transform propagation speed, etc were upstream performance issues and not issues in bevy_ecs_tilemap.

  1. autochunking is a higher level API that is fully capable of being built on top of non-auto chunking, which bevy_ecs_tilemap supports.
  2. yep. I would be disappointed if we only copied what everyone else did instead of leaning into bevy's unique strengths though, so I appreciate the entity support attempt in this PR.
  3. A superficial user interface issue and not a core problem with the crate. This is also a very common bevy pattern and spatial indexing crates like bevy_spatial also return Entity. "fixing this" is the domain of PRs like Nested Queries, not the tilemap feature: Nested Queries #21557

I still need to find time to do an actual review of the code here.

@jamescarterbell
Copy link
Contributor Author

jamescarterbell commented Dec 29, 2025

A superficial user interface issue and not a core problem with the crate.

I disagree that it's a superficial issue, I think it's a real usability issue especially when combined with the lack of autochunking.

It turns every look up into a chunked map into the following:

  1. Get the map
  2. Get the chunk
  3. Get the entity
  4. Plug that entity into your query

and this is on top of whatever chunking solution the user is opting into or writing themselves.

I think bevy_ecs_tilemap can add this as a feature to the crate and solve the issue, but it hasn't and this is something that I think is very important for a bevy native solution to get right from the start. No other engines tilemap solution makes you go through this many steps, it's usually:

  1. Get the map
  2. Get the tile

Nested Queries look great, if this setup looks right, I'd definitely want to move the queries to nested queries.

@ChristopherBiscardi
Copy link
Contributor

superficial here means it can be added by for example, writing a function, and isn't an architectural limitation. it does not mean that it isn't important.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Rendering Drawing game state to the screen C-Feature A new feature, making something new possible S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants