Crate bevy_sparse_tilemap

source ·
Expand description

# bevy_sparse_tilemap

Crates.io docs license Crates.io

A Tilemap crate for the Bevy game engine with a focus on large map sizes and ECS sparse maps

§Features

§ECS Sparse Tilemaps

bevy_sparse_tilemap only spawns the minimum amount of entities neccesary and doesn’t one-to-one map tiles to entities. This allows it to dodge current Bevy performance issues related to rendering and unlocks a substantially higher baseline performance for extra large maps.

Tiles in bevy_sparse_tilemap are stored on each chunk using custom storage. bevy_sparse_tilemap does support spawning entities mapped to specific tiles at will for when you really need to save extra information or to gain access to the ECS when needed for specific tile types.

bevy_sparse_tilemap has a built in API to handle spawning, despawning, and accessing tiles optional entities as well as tile data.

Internally this crate supports both Sparse and Dense internal storage. Both are still ECS sparse (minimal amount of entities spawned) but sparse maps don’t require the user to supply data for every tile.

§Multiple Map Types

bevy_sparse_tilemap supports different map types using a generics trait based system that provides great flexibility to support many different map types.

Currently supported:

  • Hexagon
  • Square

§Massive Map Sizes

Because of the ECS Sparse nature of the crate, bevy_sparse_tilemap dodges current limitations in Bevy related to rendering performance issues. These limitations are what other tilemap crates run into and inhibits their performance. This allows bevy_sparse_tilemap to have exceptional performance as a baseline, allowing maps as large as 15k x 15k easily with smart rendering using something like bevy_fast_tilemap.

§Tilemap Logic Only

This crate focuses purely on the tilemap logic and leaves the rendering to the user.

§Examples

See Docs.rs for documentation on how to use bevy_sparse_tilemap as well as brief examples.

See Github Examples for longer examples of each feature of the crate.

§What about bevy_ecs_tilemap?

bevy_ecs_tilemap is a fabulous crate that will probably cover most of your needs in an easier to use plugin and you should reach for that first in most situations.

You should use bevy_ecs_tilemap if:

  • You don’t need very large maps (bevy_ecs_tilemap runs into performance issues around 200x200 in my testing)
  • You want every tile to be its own Entity for ECS integration (This crate tries to avoid unnecessary entities and uses a Voxel like approach)
  • You want a more mature and more feature rich plugin
  • You want tilemap rendering handled for you

You should use bevy_sparse_tilemap if:

  • You want very very large maps, bevy_sparse_tilemap can reach substantially larger map sizes compared to bevy_ecs_tilemap. (The bevy_fast_tilemap_example currently spawns a 15000x15000 tile map and runs at around 900 fps)
  • You are willing to implement your own tilemap rendering (This crate has an example for integration with bevy_fast_tilemap however that is not currently a feature that is natively supported by this crate)

§Bevy Version

BST VersionBevy Version
0.30.14
0.20.13
0.10.13

§Usage Docs

Brief overview of core concepts of bevy_sparse_tilemap

§Cell, ChunkPos, ChunkCell

For the below example imagine we had a map that was 150 x 150 tiles and had chunks of 25 tiles each. This means the map is made up of 36 chunks each containing 625 tiles.

bevy_sparse_tilemap uses three separate “tile positions”. For most usage, you should just treat the map as one big thing and use Cell, ignoring the fact it’s chunked.

  • Cell: Represents a logical position on the map from the maps (0, 0) to the (map_size_x, map_size_y). Eg (x-120, y-57).
  • ChunkPos: Represents the position of a chunk in the map. For a Cell in (0,0) -> (24,24) the chunk that “owns” those tiles would be at (0,0). For a cell (x-120, y-57) the ChunkPos would be (4,2).
  • ChunkCell: Represents a position on the tilemap inside of a chunk. For a cell (x-120, y-57) the ChunkPos would be (4,2) and the ChunkCell would be (20, 7).

§Tilemap Construction

Create new tilemaps by using the TilemapBuilder. This helper manages splitting data across chunks, spawning chunks, map, adding new layers, and more related helpers.

You should always use the tilemap builder unless for some reason you absolutely cannot (please open an issue if you run into this).


fn spawn_tilemap(mut commands: Commands) {

    let mut tilemap_builder = TilemapBuilder::<TileData, MapLayers, SquareChunkLayer<TileData>, SquareMapData,
        >::new(
        TilemapLayer::new_dense_default(10000, 10000),
        SquareMapData {
            max_chunk_size: UVec2::new(100, 100)
            },
        SquareChunkSettings {
            max_chunk_size: UVec2 { x: 100, y: 100 },
            }
    );

    let Some(tilemap) = tilemap_builder.spawn_tilemap(&mut commands)
        else {
            return;
    };
}

§Tilemap Access

bevy_sparse_tilemap includes a handy [TilemapManager](crate::tilemap_manager::TilemapManager) system param that has a bevy of helper functions to make accessing, editing, and interacting with tilemaps that much easier. You don’t technically need to use this to interact with the maps however it makes it substantially easier as the guts of this crate can be a bit convuluted.


 fn access(mut tilemap_manager: TilemapManager<TileData, MapLayers, SquareChunkLayer<TileData>, SquareMapData>, mut commands: Commands, map_entity: Res<MapEntity>) {
    // We have to set the TilemapManager to the desired tilemap
    tilemap_manager.set_tilemap_entity(map_entity.0);
    // And set the manager to whatever layer we want to affect. Defaults to the default layer of the enum
    tilemap_manager.set_layer(MapLayers::Main);
    let tile_data = tilemap_manager.get_tile_data(Cell::new(9,16)).unwrap();

    // do something with the tilemap access here

 }

Modules§

Derive Macros§