Small improvements for directional navigation (#17395)
# Objective While working on more complex directional navigation work, I noticed a few small things. ## Solution Rather than stick them in a bigger PR, split them out now. - Include more useful information when responding to `DirectionalNavigationError`. - Use the less controversial `Click` events (rather than `Pressed`) in the example - Implement add_looping_edges in terms of `add_edges`. Thanks @rparrett for the idea. ## Testing Ran the `directional_navigation` example and things still work.
This commit is contained in:
parent
b693362b0c
commit
3737f86d84
@ -186,10 +186,11 @@ impl DirectionalNavigationMap {
|
||||
///
|
||||
/// This is useful for creating a circular navigation path between a set of entities, such as a menu.
|
||||
pub fn add_looping_edges(&mut self, entities: &[Entity], direction: CompassOctant) {
|
||||
for i in 0..entities.len() {
|
||||
let a = entities[i];
|
||||
let b = entities[(i + 1) % entities.len()];
|
||||
self.add_symmetrical_edge(a, b, direction);
|
||||
self.add_edges(entities, direction);
|
||||
if let Some((first_entity, rest)) = entities.split_first() {
|
||||
if let Some(last_entity) = rest.last() {
|
||||
self.add_symmetrical_edge(*last_entity, *first_entity, direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,14 +228,17 @@ impl DirectionalNavigation<'_> {
|
||||
/// If the result was `Ok`, the [`InputFocus`] resource is updated to the new focus as part of this method call.
|
||||
pub fn navigate(
|
||||
&mut self,
|
||||
octant: CompassOctant,
|
||||
direction: CompassOctant,
|
||||
) -> Result<Entity, DirectionalNavigationError> {
|
||||
if let Some(current_focus) = self.focus.0 {
|
||||
if let Some(new_focus) = self.map.get_neighbor(current_focus, octant) {
|
||||
if let Some(new_focus) = self.map.get_neighbor(current_focus, direction) {
|
||||
self.focus.set(new_focus);
|
||||
Ok(new_focus)
|
||||
} else {
|
||||
Err(DirectionalNavigationError::NoNeighborInDirection)
|
||||
Err(DirectionalNavigationError::NoNeighborInDirection {
|
||||
current_focus,
|
||||
direction,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Err(DirectionalNavigationError::NoFocus)
|
||||
@ -249,8 +253,13 @@ pub enum DirectionalNavigationError {
|
||||
#[error("No focusable entity is currently set.")]
|
||||
NoFocus,
|
||||
/// No neighbor in the requested direction.
|
||||
#[error("No neighbor in the requested direction.")]
|
||||
NoNeighborInDirection,
|
||||
#[error("No neighbor from {current_focus} in the {direction:?} direction.")]
|
||||
NoNeighborInDirection {
|
||||
/// The entity that was the focus when the error occurred.
|
||||
current_focus: Entity,
|
||||
/// The direction in which the navigation was attempted.
|
||||
direction: CompassOctant,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -5,6 +5,8 @@
|
||||
//!
|
||||
//! In this example, we will set up a simple UI with a grid of buttons that can be navigated using the arrow keys or gamepad input.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::{
|
||||
input_focus::{
|
||||
directional_navigation::{
|
||||
@ -65,9 +67,7 @@ const FOCUSED_BORDER: Srgba = bevy::color::palettes::tailwind::BLUE_50;
|
||||
// In a real project, each button would also have its own unique behavior,
|
||||
// to capture the actual intent of the user
|
||||
fn universal_button_click_behavior(
|
||||
// We're using an on-mouse-down trigger to improve responsiveness;
|
||||
// Clicked is better when you want roll-off cancellation
|
||||
mut trigger: Trigger<Pointer<Pressed>>,
|
||||
mut trigger: Trigger<Pointer<Click>>,
|
||||
mut button_query: Query<(&mut BackgroundColor, &mut ResetTimer)>,
|
||||
) {
|
||||
let button_entity = trigger.target();
|
||||
@ -368,7 +368,7 @@ fn highlight_focused_element(
|
||||
}
|
||||
}
|
||||
|
||||
// By sending a Pointer<Pressed> trigger rather than directly handling button-like interactions,
|
||||
// By sending a Pointer<Click> trigger rather than directly handling button-like interactions,
|
||||
// we can unify our handling of pointer and keyboard/gamepad interactions
|
||||
fn interact_with_focused_button(
|
||||
action_state: Res<ActionState>,
|
||||
@ -381,7 +381,7 @@ fn interact_with_focused_button(
|
||||
{
|
||||
if let Some(focused_entity) = input_focus.0 {
|
||||
commands.trigger_targets(
|
||||
Pointer::<Pressed> {
|
||||
Pointer::<Click> {
|
||||
target: focused_entity,
|
||||
// We're pretending that we're a mouse
|
||||
pointer_id: PointerId::Mouse,
|
||||
@ -395,7 +395,7 @@ fn interact_with_focused_button(
|
||||
),
|
||||
position: Vec2::ZERO,
|
||||
},
|
||||
event: Pressed {
|
||||
event: Click {
|
||||
button: PointerButton::Primary,
|
||||
// This field isn't used, so we're just setting it to a placeholder value
|
||||
hit: HitData {
|
||||
@ -404,6 +404,7 @@ fn interact_with_focused_button(
|
||||
position: None,
|
||||
normal: None,
|
||||
},
|
||||
duration: Duration::from_secs_f32(0.1),
|
||||
},
|
||||
},
|
||||
focused_entity,
|
||||
|
Loading…
Reference in New Issue
Block a user