use bevy::{ image::{ ImageArrayLayout, ImageLoaderSettings, }, prelude::*, sprite_render::{ TileData, TilemapChunk, TilemapChunkTileData, }, }; use bevy_rapier2d::prelude::*; use crate::meters; const TILEMAP_PATH: &'static str = "sprites/level/tilemap.png"; const TILE_DISPLAY_SIZE: UVec2 = UVec2::splat(meters(1.) as u32); #[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)] #[reflect(Component, Debug, PartialEq, Default, Clone)] pub struct Tilemap; pub fn tilemap_bundle( asset_server: &Res, tiles: Vec<(u16, URect)>, ) -> impl Bundle { let mut size = uvec2(0, 0); for tile in tiles.iter() { size.x = size.x.max(tile.1.max.x + 1); size.y = size.y.max(tile.1.max.y + 1); } let tile_data: Vec> = (0..size.element_product()).map(|xy| { if let Some((id, _)) = tiles.iter().find(|(_, rect)| rect.contains(uvec2(xy % size.x, xy / size.x))) { Some(TileData::from_tileset_index(*id)) } else { None } }).collect(); ( Tilemap, TilemapChunk { chunk_size: size, tile_display_size: TILE_DISPLAY_SIZE, tileset: asset_server.load_with_settings( TILEMAP_PATH, |settings: &mut ImageLoaderSettings| { settings.array_layout = Some(ImageArrayLayout::RowCount { rows: 3 }) }, ), ..default() }, Transform::from_xyz( meters(0.5) * size.x as f32, meters(0.5) * size.y as f32 - meters(1.), 0., ), TilemapChunkTileData(tile_data), Children::spawn(SpawnIter(tiles.into_iter().map(move |(_, rect)| { let mut rect = rect.as_rect(); rect.max = vec2(rect.max.x + 1., rect.max.y + 1.); let (width, height) = (rect.width(), rect.height()); let offset = rect.center(); ( Collider::cuboid(meters(width * 0.5), meters(height * 0.5)), Transform::from_xyz( meters(offset.x - size.x as f32 * 0.5), meters(offset.y - size.y as f32 * 0.5), 0., ), )} ))), ) }