Skip to content

Commit

Permalink
Improve minimap camera controls (#642)
Browse files Browse the repository at this point in the history
* Minimap no longer registers camera input when mouse is pressed down
   elsewhere and brought over the minimap.
* Minimap camera responds to press instead of release.
* Camera can now be dragged across the minimap in addition to clicking.
  • Loading branch information
Zakru authored Sep 7, 2023
1 parent 1697c32 commit 0202976
Showing 1 changed file with 128 additions and 41 deletions.
169 changes: 128 additions & 41 deletions crates/controller/src/hud/minimap/interaction.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::fmt;

use bevy::{
input::{mouse::MouseButtonInput, ButtonState},
input::{
mouse::{MouseButtonInput, MouseMotion},
ButtonState,
},
prelude::*,
window::PrimaryWindow,
};
Expand All @@ -19,35 +22,47 @@ pub(super) struct InteractionPlugin;

impl Plugin for InteractionPlugin {
fn build(&self, app: &mut App) {
app.add_event::<MinimapClickEvent>().add_systems(
InputSchedule,
(
click_handler.in_set(InteractionSet::ClickHandler),
move_camera_system.after(InteractionSet::ClickHandler),
send_units_system
.after(InteractionSet::ClickHandler)
.before(CommandsSet::SendSelected),
delivery_location_system
.after(InteractionSet::ClickHandler)
.before(CommandsSet::DeliveryLocation),
)
.run_if(in_state(GameState::Playing)),
);
app.add_event::<MinimapPressEvent>()
.add_event::<MinimapDragEvent>()
.insert_resource(DraggingButtons(Vec::new()))
.add_systems(
InputSchedule,
(
press_handler
.in_set(InteractionSet::PressHandler)
.run_if(on_event::<MouseButtonInput>()),
drag_handler
.in_set(InteractionSet::DragHandler)
.after(InteractionSet::PressHandler)
.run_if(on_event::<MouseMotion>()),
move_camera_system
.after(InteractionSet::PressHandler)
.after(InteractionSet::DragHandler),
send_units_system
.after(InteractionSet::PressHandler)
.before(CommandsSet::SendSelected),
delivery_location_system
.after(InteractionSet::PressHandler)
.before(CommandsSet::DeliveryLocation),
)
.run_if(in_state(GameState::Playing)),
);
}
}

#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemSet)]
enum InteractionSet {
ClickHandler,
PressHandler,
DragHandler,
}

#[derive(Event)]
struct MinimapClickEvent {
struct MinimapPressEvent {
button: MouseButton,
position: Vec2,
}

impl MinimapClickEvent {
impl MinimapPressEvent {
fn new(button: MouseButton, position: Vec2) -> Self {
Self { button, position }
}
Expand All @@ -63,68 +78,140 @@ impl MinimapClickEvent {
}
}

impl fmt::Debug for MinimapClickEvent {
impl fmt::Debug for MinimapPressEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?} -> {:?}", self.button, self.position)
}
}

fn click_handler(
#[derive(Event)]
struct MinimapDragEvent {
button: MouseButton,
position: Vec2,
}

impl MinimapDragEvent {
fn new(button: MouseButton, position: Vec2) -> Self {
Self { button, position }
}

fn button(&self) -> MouseButton {
self.button
}

/// Position on the map in 2D flat coordinates (these are not minimap
/// coordinates).
fn position(&self) -> Vec2 {
self.position
}
}

#[derive(Resource, Deref, DerefMut)]
struct DraggingButtons(Vec<MouseButton>);

fn press_handler(
window_query: Query<&Window, With<PrimaryWindow>>,
mut input_events: EventReader<MouseButtonInput>,
hud: HudNodes<With<MinimapNode>>,
bounds: Res<MapBounds>,
mut click_events: EventWriter<MinimapClickEvent>,
mut dragging: ResMut<DraggingButtons>,
mut press_events: EventWriter<MinimapPressEvent>,
) {
let Some(cursor) = window_query.single().cursor_position() else {
return;
};
let cursor = window_query.single().cursor_position();

for event in input_events.iter() {
if event.state != ButtonState::Released {
continue;
match event.state {
ButtonState::Released => {
dragging.retain(|b| *b != event.button);
continue;
}
ButtonState::Pressed => (),
}

let Some(cursor) = cursor else {
continue;
};

if let Some(mut relative) = hud.relative_position(cursor) {
dragging.push(event.button);
relative.y = 1. - relative.y;
let event = MinimapClickEvent::new(event.button, bounds.rel_to_abs(relative));
info!("Sending minimap click event {event:?}.");
click_events.send(event);
let event = MinimapPressEvent::new(event.button, bounds.rel_to_abs(relative));
info!("Sending minimap press event {event:?}.");
press_events.send(event);
}
}
}

fn drag_handler(
window_query: Query<&Window, With<PrimaryWindow>>,
hud: HudNodes<With<MinimapNode>>,
bounds: Res<MapBounds>,
dragging: Res<DraggingButtons>,
mut drag_events: EventWriter<MinimapDragEvent>,
) {
if dragging.is_empty() {
return;
}

let Some(cursor) = window_query.single().cursor_position() else {
return;
};

if let Some(relative) = hud.relative_position(cursor) {
let proportional = Vec2::new(relative.x, 1. - relative.y);
let world = bounds.rel_to_abs(proportional);

for button in &**dragging {
let event = MinimapDragEvent::new(*button, world);
drag_events.send(event);
}
}
}

fn move_camera_system(
mut click_events: EventReader<MinimapClickEvent>,
mut press_events: EventReader<MinimapPressEvent>,
mut drag_events: EventReader<MinimapDragEvent>,
mut camera_events: EventWriter<MoveFocusEvent>,
) {
for click in click_events.iter() {
if click.button() != MouseButton::Left {
for press in press_events.iter() {
if press.button() != MouseButton::Left {
continue;
}
camera_events.send(MoveFocusEvent::new(click.position()));

let event = MoveFocusEvent::new(press.position());
camera_events.send(event);
}

for drag in drag_events.iter() {
if drag.button() != MouseButton::Left {
continue;
}

let event = MoveFocusEvent::new(drag.position());
camera_events.send(event);
}
}

fn send_units_system(
mut click_events: EventReader<MinimapClickEvent>,
mut press_events: EventReader<MinimapPressEvent>,
mut send_events: EventWriter<SendSelectedEvent>,
) {
for click in click_events.iter() {
if click.button() != MouseButton::Right {
for press in press_events.iter() {
if press.button() != MouseButton::Right {
continue;
}
send_events.send(SendSelectedEvent::new(click.position()));
send_events.send(SendSelectedEvent::new(press.position()));
}
}

fn delivery_location_system(
mut click_events: EventReader<MinimapClickEvent>,
mut press_events: EventReader<MinimapPressEvent>,
mut location_events: EventWriter<DeliveryLocationSelectedEvent>,
) {
for click in click_events.iter() {
if click.button() != MouseButton::Right {
for press in press_events.iter() {
if press.button() != MouseButton::Right {
continue;
}
location_events.send(DeliveryLocationSelectedEvent::new(click.position()));
location_events.send(DeliveryLocationSelectedEvent::new(press.position()));
}
}

0 comments on commit 0202976

Please sign in to comment.