Improve selection system (#78)
All checks were successful
Rust Checks / checks (push) Successful in 4m33s

closes: #66
Reviewed-on: fish-canard/border-wars#78
Reviewed-by: Raphaël <r.lauray@outlook.fr>
This commit is contained in:
CoCo_Sol 2024-03-19 15:08:41 +00:00
parent 64280ab3ff
commit 79092ea0f5
4 changed files with 49 additions and 23 deletions

View file

@ -2,8 +2,8 @@
use bevy::prelude::*;
use border_wars::camera::CameraPlugin;
use border_wars::map::click_tile::TilesClickable;
use border_wars::map::renderer::RendererPlugin;
use border_wars::map::selected_tile::SelectTilePlugin;
use border_wars::scenes::ScenesPlugin;
fn main() {
@ -12,6 +12,6 @@ fn main() {
.add_plugins(ScenesPlugin)
.add_plugins(RendererPlugin)
.add_plugins(CameraPlugin)
.add_plugins(TilesClickable)
.add_plugins(SelectTilePlugin)
.run();
}

View file

@ -1,9 +1,9 @@
//! Contains all the logic related to the map.
pub mod click_tile;
pub mod generation;
pub mod hex;
pub mod renderer;
pub mod selected_tile;
use bevy::prelude::*;

View file

@ -21,7 +21,7 @@ impl Plugin for RendererPlugin {
/// The gap between the center of the tiles in the map.
#[derive(Resource)]
struct TilesGap(Vec2);
pub struct TilesGap(pub Vec2);
/// The size of the tiles in the map.
#[derive(Resource, Clone, Copy)]
@ -85,7 +85,7 @@ fn render_map(
commands.entity(entity).insert(SpriteBundle {
sprite: Sprite {
anchor: Anchor::BottomLeft,
anchor: Anchor::BottomCenter,
..default()
},
texture,

View file

@ -1,15 +1,10 @@
//! All programs related to the clicking on a tile.
//! All programs related to the selection of a tile.
use bevy::prelude::*;
use super::renderer::TilesGap;
use super::Tile;
/// The event that is triggered when a tile is clicked.
///
/// The event contains the index (ID) of the clicked tile.
#[derive(Event)]
pub struct TileJustClicked(pub u32);
/// An event that is triggered when a mouse button is clicked.
///
/// The event contains the position of the cursor in the world.
@ -21,15 +16,37 @@ struct ClickOnTheWorld(Vec2);
#[derive(Component)]
pub struct ZoneNotClickable;
/// A plugin that handles the selection of tiles.
pub struct TilesClickable;
/// The currently selected tile.
#[derive(Resource, Default, Debug)]
pub enum SelectedTile {
/// The entity of the selected tile.
Tile(Entity),
impl Plugin for TilesClickable {
/// Zero tile selected.
#[default]
None,
}
impl SelectedTile {
/// Returns the entity of the selected tile.
/// Returns `None` if no tile is selected.
pub const fn get_entity(&self) -> Option<Entity> {
match self {
Self::Tile(entity) => Some(*entity),
Self::None => None,
}
}
}
/// A plugin that handles the selection of tiles.
pub struct SelectTilePlugin;
impl Plugin for SelectTilePlugin {
fn build(&self, app: &mut App) {
app.add_systems(PreUpdate, mouse_handler)
.add_systems(PreUpdate, select_closest_tile)
.add_event::<ClickOnTheWorld>()
.add_event::<TileJustClicked>();
.init_resource::<SelectedTile>();
}
}
@ -73,25 +90,30 @@ fn mouse_handler(
events_writer.send(ClickOnTheWorld(cursor_position_in_world));
}
/// Get the closest tile to the cursor and send it in an event.
/// Get the closest tile to the cursor and select it.
fn select_closest_tile(
tiles: Query<(Entity, &Transform, &Tile)>,
mut click_event_reader: EventReader<ClickOnTheWorld>,
mut clicked_tile_event_writer: EventWriter<TileJustClicked>,
tile_gap: Res<TilesGap>,
mut current_entity: ResMut<SelectedTile>,
) {
for click_event in click_event_reader.read() {
// The closest tile and its distance to the cursor.
// The closest tile and its position.
let mut closest_entity: Option<Entity> = None;
let mut closest_position: Option<f32> = None;
// To keep the aspect ratio.
let click_position = click_event.0 / tile_gap.0;
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 += (tile_size / 2.0) * tile_scale;
let mut tile_position = tile_transform.translation.truncate() / tile_gap.0;
// The origine of the tile is the bottom center.
tile_position.y += (tile_size.y / 2.0) * tile_scale.y / tile_gap.0.y;
let distance_to_cursor = tile_position.distance(click_event.0);
let distance_to_cursor = tile_position.distance(click_position);
if closest_position.is_none() || closest_position > Some(distance_to_cursor) {
closest_entity = Some(tile_entity);
@ -99,7 +121,11 @@ fn select_closest_tile(
}
}
if let Some(tile_entity) = closest_entity {
clicked_tile_event_writer.send(TileJustClicked(tile_entity.index()));
if current_entity.get_entity() == Some(tile_entity) {
*current_entity = SelectedTile::None;
} else {
*current_entity = SelectedTile::Tile(tile_entity);
}
}
}
}