use bevy::{ diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, math::Vec2Swizzles, prelude::*, sprite::SpriteBundle, window::PrimaryWindow, DefaultPlugins, }; use bevy_tiles::{ commands::TileCommandExt, coords::{calculate_chunk_coordinate, world_to_tile, CoordIterator}, maps::TileMap, tiles_2d::*, TilesPlugin, }; use std::ops::{Deref, DerefMut}; fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugins(TilesPlugin) .add_plugins(LogDiagnosticsPlugin::default()) .add_plugins(FrameTimeDiagnosticsPlugin) .add_systems(Startup, spawn) .add_systems(Update, (add_damage, check_damage).chain()) .add_systems(PostUpdate, sync_tile_transforms) .run(); } #[derive(Component)] struct Block; #[derive(Component)] struct Damage(usize); impl Deref for Damage { type Target = usize; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Damage { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } #[derive(Component)] struct GameLayer; fn spawn(mut commands: Commands, asset_server: Res) { let block = asset_server.load("block.png"); commands.spawn(Camera2dBundle { projection: OrthographicProjection { scale: 1.0, ..Camera2dBundle::default().projection }, ..Default::default() }); let mut tile_commands = commands.spawn_map(32, GameLayer); let sprite_bundle = SpriteBundle { texture: block, ..Default::default() }; let size = 200; tile_commands.spawn_tile_batch( CoordIterator::new([-size, -size], [size, size]), move |_| (Block, sprite_bundle.clone()), ); } fn add_damage( mut commands: Commands, mut block_maps: TileMapQuery<(Entity, Option<&mut Damage>), With>, map: Query<(Entity, &TileMap), With>, windows: Query<&Window, With>, camera: Query<(&Camera, &GlobalTransform)>, buttons: Res>, ) { let (map_id, map) = map.single(); let (cam, cam_t) = camera.single(); let mut blocks = block_maps.get_map_mut(map_id).unwrap(); let cursor_pos = windows .single() .cursor_position() .and_then(|cursor| cam.viewport_to_world(cam_t, cursor.xy())) .map(|ray| ray.origin.truncate()) .map(|pos| world_to_tile(pos, 16.0)); if let Some(damage_pos) = buttons .just_pressed(MouseButton::Left) .then_some(cursor_pos) .flatten() { let start = [damage_pos[0] - 2, damage_pos[1] - 2]; let end = [damage_pos[0] + 2, damage_pos[1] + 2]; for (block_id, damage) in blocks.iter_in_mut(start, end) { if let Some(mut damage) = damage { **damage += 1; } else { commands.entity(block_id).insert(Damage(1)); } } } if let Some(damage_pos) = buttons .just_pressed(MouseButton::Right) .then_some(cursor_pos) .flatten() { let chunk_c = calculate_chunk_coordinate(damage_pos, map.get_chunk_size()); commands.tile_map(map_id).despawn_chunk(chunk_c); } } fn check_damage( mut commands: Commands, mut damaged: Query<(Entity, &Damage, &mut Sprite), Changed>, ) { for (id, damage, mut sprite) in damaged.iter_mut() { match **damage { x if x > 3 => commands.entity(id).despawn(), x => { let tint = 1.0 - x as f32 / 3.0; sprite.color = Color::Rgba { red: 1.0, green: tint, blue: tint, alpha: 1.0, } } } } } fn sync_tile_transforms(mut tiles: Query<(&TileCoord, &mut Transform), Changed>) { for (tile_c, mut transform) in tiles.iter_mut() { transform.translation.x = tile_c[0] as f32 * 16.0; transform.translation.y = tile_c[1] as f32 * 16.0; } }