bevy_ecs_tilemap
bevy_ecs_tilemap copied to clipboard
What happened to MapQuery in 0.7.0?
I've just updated Bevy to 0.8.0 and bevy_ecs_tilemap to 0.7.0, but there no longer seems to be a MapQuery (or indeed a lot of the structs which were in the docs last version). Is there a migration guide of some sort?
Hey @Caligari, I just finished migrating a project of mine over. One of the ways I was able to ease the pain of migration was by implementing my own Layer component:
use bevy::prelude::*;
/// The base layer identifier is the primary base tile layer, used for background or root level
/// background tile rendering.
pub const BASE_LAYER: f32 = 0.0f32;
/// The world layer is the tile layer where belts are rendered and tile entities
/// are constructed for game logic.
pub const WORLD_LAYER: f32 = 1.0f32;
/// The debug layer is where the debug grid render occurs.
pub const DEBUG_LAYER: f32 = 2.0f32;
/// Trait used to identify map layers in the world and can be used to constrain generic parameters
/// for world layers.
pub trait Layer {
/// Returns the z-index for the layer.
fn z_index() -> f32;
}
#[derive(Component)]
pub struct MapLayer;
impl Layer for MapLayer {
fn z_index() -> f32 {
BASE_LAYER
}
}
#[derive(Component)]
pub struct WorldLayer;
impl Layer for WorldLayer {
fn z_index() -> f32 {
WORLD_LAYER
}
}
#[derive(Component)]
pub struct DebugLayer;
impl Layer for DebugLayer {
fn z_index() -> f32 {
DEBUG_LAYER
}
}
And then I added a SystemParameter
type similar to the old MapQuery
that looks like this:
use bevy::prelude::*;
use bevy_ecs_tilemap::prelude::*;
use bevy::ecs::system::SystemParam;
use crate::world::Layer;
#[derive(SystemParam)]
pub struct TileQuery<'w, 's, T: Component + Layer> {
map: Query<'w, 's, &'static TileStorage, With<T>>,
}
impl<'w, 's, T: Component + Layer> TileQuery<'w, 's, T> {
/// This method returns the tile entity for the specific tile position in the layer.
#[inline]
pub fn tile_entity(&mut self, tile: UVec2) -> Option<Entity> {
if let Ok(storage) = self.map.get_single() {
let tile_pos = TilePos::new(tile.x, tile.y);
storage.get(&tile_pos)
} else {
None
}
}
}
Then wherever I had a MapQuery
in my system parameters, I replaced with TileQuery<T>
where T
is a Layer
implementation.
The other change I had to make is by adding the specific Layer
to the added entities. For example:
// Add World Layer
commands
.entity(world_layer_entity)
.insert_bundle(TilemapBundle {
grid_size,
size: map_size,
storage: world_tile_storage,
texture: TilemapTexture(belts_texture),
tile_size,
transform: helpers::get_centered_transform_2d(
&map_size,
&tile_size,
WORLD_LAYER,
),
..Default::default()
})
.insert(WorldLayer); // <-- Insert the WorldLayer here.
Hope this helps. It's not exactly equivalent to the MapQuery
(I think MapQuery
let you supply a layer id parameter on get_tile_entity
), but it was sufficient for my use case.
Thanks, for that, @mbolt35! I'll see what I need.
I'm trying to work through the git history, but the examples were moved into an old_examples folder and deleted, so the history doesn't go back so I could see what changes were made. Just working out what my startup should now do, based on the changes I made to the previous example code, is a little confusing, but I'm getting there.
I'm making notes, which I will post here, if there isn't something more authoratative.
I'll work on creating a migration guide, but I'm not sure how useful it'll be since almost everything has changed.
So, several things have changed with the move to 0.7.0. I'm going to focus on the startup/set up, as that's where most of the confusion was, for me.
In the samples, MapQuery
was used in startup to build layers, which is now handled differently. So, to update you need to:
- remove
MapQuery
from startup parameters -
asset_server.load
now needs a type ofHandle<Image>
to get its return value into the form we need for a tilemap; for example:
let texture_handle: Handle<Image> = asset_server.load(FILENAME);
- (old)
MapSize
andChunkSize
are now (new) combined intoTilemapSize
; for example:
MapSize(10,10), ChunkSize(64, 64)
would now belet timemap_size = TilemapSize(x:640, y:640);
- (new)
let mut tile_storage = TileStorage::empty(tilemap_size);
(using theTilemapSize
, above) - (new)
let tilemap_id = TilemapId(tilemap_entity);
(where the tilemap_entity existed in the previous version) - (old)
layer_builder.fill()
calls become (new)bevy_ecs_tilemap::helpers::fill_tilemap_rect()
calls using this format:
bevy_ecs_tilemap::helpers::fill_tilemap_rect( TileTexture(0), TilePos { x: 0, y: 0 }, TilemapSize { x: 128, y: 128 }, tilemap_id, &mut commands, &mut tile_storage, );
- (new)
let tile_size = TilemapTileSize { x: 17.0, y: 15.0 }; let grid_size = TilemapGridSize { x: 17.0, y: 15.0 };
(these seem to need to be the same, in most cases, and are directly tied to the individual tile size in pixels in the tilemap; previously this was theTileSize
part of theLayerSettings
) - (old)
commands.insert(map)
becomes (new)commands.insert_bundle(TilemapBundle{})
using the various items noted above. For example:
commands .entity(tilemap_entity) .insert_bundle(TilemapBundle { grid_size, size: tilemap_size, storage: tile_storage, texture: TilemapTexture(texture_handle), tile_size, mesh_type: TilemapMeshType::Hexagon(HexType::Column), ..Default::default() });
Together these changes got me going again, but I suspect there are other differences in usage that I had not run into. Hopefully this will help anyone else who is starting on that transition. though.