save
Some checks failed
Rust Checks / checks (push) Has been cancelled

This commit is contained in:
CoCo_Sol 2024-03-06 15:58:43 +01:00
parent d378e8f44c
commit 179054a6cc
5 changed files with 142 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

View file

@ -4,6 +4,7 @@ use bevy::prelude::*;
use border_wars::camera::CameraPlugin; use border_wars::camera::CameraPlugin;
use border_wars::map::renderer::RendererPlugin; use border_wars::map::renderer::RendererPlugin;
use border_wars::scenes::ScenesPlugin; use border_wars::scenes::ScenesPlugin;
use border_wars::map::selection::SelectorPlugin;
fn main() { fn main() {
App::new() App::new()
@ -11,5 +12,6 @@ fn main() {
.add_plugins(ScenesPlugin) .add_plugins(ScenesPlugin)
.add_plugins(RendererPlugin) .add_plugins(RendererPlugin)
.add_plugins(CameraPlugin) .add_plugins(CameraPlugin)
.add_plugins(SelectorPlugin)
.run(); .run();
} }

View file

@ -3,6 +3,7 @@
pub mod generation; pub mod generation;
pub mod hex; pub mod hex;
pub mod renderer; pub mod renderer;
pub mod selection;
use bevy::prelude::*; use bevy::prelude::*;

View file

@ -40,7 +40,7 @@ impl Tile {
/// ///
/// TODO: we are currently using temporary images that will modify /// TODO: we are currently using temporary images that will modify
/// this function in the future. /// this function in the future.
const fn get_image_size(&self) -> Vec2 { pub const fn get_image_size(&self) -> Vec2 {
match self { match self {
Self::Grass => Vec2 { Self::Grass => Vec2 {
x: 1250.0, x: 1250.0,

View file

@ -0,0 +1,138 @@
//! All programs related to the selection of tiles.
use bevy::prelude::*;
use bevy::sprite::Anchor;
use super::Tile;
/// A component that represents a selected tile.
#[derive(Component)]
pub struct SelectedTile;
/// A component that represents the selection.
#[derive(Component)]
struct Selection;
/// A event that is triggered when a mouse button is clicked in the world.
///
/// The event contains the position of the cursor in the world.
#[derive(Event)]
struct ClickOnTheWorld(Vec2);
/// A plugin that handles and render the selection of tiles.
pub struct SelectorPlugin;
impl Plugin for SelectorPlugin {
fn build(&self, app: &mut App) {
app.add_systems(PreUpdate, mouse_handler)
.add_systems(PreUpdate, select_closest_tile)
.add_systems(Update, render_selection)
.add_event::<ClickOnTheWorld>();
}
}
/// Handles the mouse click and gets the position of the cursor in the world.*
/// Finally, it sends an event with the position of the cursor.
fn mouse_handler(
mouse_button_input: Res<Input<MouseButton>>,
windows: Query<&Window>,
cameras: Query<(&Camera, &GlobalTransform)>,
mut events_writer: EventWriter<ClickOnTheWorld>,
) {
if !mouse_button_input.just_pressed(MouseButton::Left) {
return;
}
let cursor_position_on_screen = windows
.get_single()
.expect("Main window not found")
.cursor_position();
let Some(cursor_position_on_screen) = cursor_position_on_screen else {
return;
};
let (camera, camera_transform) = cameras.get_single().expect("Camera not found");
let cursor_position_in_world = camera
.viewport_to_world(camera_transform, cursor_position_on_screen)
.expect("Failed to convert cursor position")
.origin
.truncate();
events_writer.send(ClickOnTheWorld(cursor_position_in_world));
}
/// Selects the closest tile to the cursor and marks it as selected.
/// It also marks the old selection as unselected.
fn select_closest_tile(
mut commands: Commands,
tiles: Query<(Entity, &Transform, &Tile)>,
mut old_selection: Query<Entity, With<SelectedTile>>,
mut selected_tile: Local<Option<u32>>,
mut click_event_reader: EventReader<ClickOnTheWorld>,
) {
for click_event in click_event_reader.read() {
// The closest tile and its distance to the cursor.
let mut closest_entity: Option<Entity> = None;
let mut closest_position: Option<f32> = None;
for (tile_entity, tile_transform, tile_type) in tiles.iter() {
let mut tile_position = tile_transform.translation.truncate();
let tile_size = tile_type.get_image_size();
let tile_scale = tile_transform.scale.truncate();
tile_position.x -= (tile_size.x / 2.0) * tile_scale.x;
tile_position.y += (tile_size.y / 2.0) * tile_scale.y;
let distance_to_cursor = tile_position.distance(click_event.0);
if closest_position.is_none() || closest_position > Some(distance_to_cursor) {
closest_entity = Some(tile_entity);
closest_position = Some(distance_to_cursor);
}
}
if let Some(tile_entity) = closest_entity {
if *selected_tile == Some(tile_entity.index()) {
return;
}
commands.entity(tile_entity).insert(SelectedTile);
*selected_tile = Some(tile_entity.index());
}
for entity in old_selection.iter_mut() {
commands.entity(entity).remove::<SelectedTile>();
}
}
}
/// Renders the selection with a sprite.
///
/// The position updates when the selected tile changes.
fn render_selection(
mut commands: Commands,
asset_server: Res<AssetServer>,
query: Query<&Transform, Added<SelectedTile>>,
mut selection_query: Query<&mut Transform, (With<Selection>, Without<SelectedTile>)>,
) {
for target_transform in query.iter() {
let mut transform = *target_transform;
transform.translation.z += 1.0;
if selection_query.is_empty() {
commands
.spawn(SpriteBundle {
sprite: Sprite {
anchor: Anchor::BottomRight,
..Default::default()
},
transform,
// TODO: This image is a temporary image of the selection.
texture: asset_server.load("selection.png"),
..default()
})
.insert(Selection);
} else if let Ok(mut selection_transform) = selection_query.get_single_mut() {
*selection_transform = transform;
}
}
}