From 72f70745c5883b0530a42241200c7aa2acf7230e Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 15 Jan 2025 18:43:03 -0500 Subject: [PATCH] `add_edges` helper for directional navigation (#17389) # Objective While `add_looping_edges` is a helpful method for manually defining directional navigation maps, we don't always want to loop around! ## Solution Add a non-looping variant. These commits are cherrypicked from the more complex #17247. ## Testing I've updated the `directional_navigation` example to use these changes, and verified that it works. --------- Co-authored-by: Rob Parrett Co-authored-by: Benjamin Brienen --- .../src/directional_navigation.rs | 30 ++++++++++++++++++- examples/ui/directional_navigation.rs | 16 ++++------ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/crates/bevy_input_focus/src/directional_navigation.rs b/crates/bevy_input_focus/src/directional_navigation.rs index 1ee2f4ca73..17114ef11a 100644 --- a/crates/bevy_input_focus/src/directional_navigation.rs +++ b/crates/bevy_input_focus/src/directional_navigation.rs @@ -173,7 +173,16 @@ impl DirectionalNavigationMap { self.add_edge(b, a, direction.opposite()); } - /// Add symmetrical edges between all entities in the provided slice, looping back to the first entity at the end. + /// Add symmetrical edges between each consecutive pair of entities in the provided slice. + /// + /// Unlike [`add_looping_edges`](Self::add_looping_edges), this method does not loop back to the first entity. + pub fn add_edges(&mut self, entities: &[Entity], direction: CompassOctant) { + for pair in entities.windows(2) { + self.add_symmetrical_edge(pair[0], pair[1], direction); + } + } + + /// Add symmetrical edges between each consecutive pair of entities in the provided slice, looping back to the first entity at the end. /// /// 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) { @@ -342,6 +351,25 @@ mod tests { assert_eq!(map.get_neighbor(c, CompassOctant::West), None); } + #[test] + fn edges() { + let mut world = World::new(); + let a = world.spawn_empty().id(); + let b = world.spawn_empty().id(); + let c = world.spawn_empty().id(); + + let mut map = DirectionalNavigationMap::default(); + map.add_edges(&[a, b, c], CompassOctant::East); + + assert_eq!(map.get_neighbor(a, CompassOctant::East), Some(b)); + assert_eq!(map.get_neighbor(b, CompassOctant::East), Some(c)); + assert_eq!(map.get_neighbor(c, CompassOctant::East), None); + + assert_eq!(map.get_neighbor(a, CompassOctant::West), None); + assert_eq!(map.get_neighbor(b, CompassOctant::West), Some(a)); + assert_eq!(map.get_neighbor(c, CompassOctant::West), Some(b)); + } + #[test] fn looping_edges() { let mut world = World::new(); diff --git a/examples/ui/directional_navigation.rs b/examples/ui/directional_navigation.rs index d80b24f3b7..b62c2875c5 100644 --- a/examples/ui/directional_navigation.rs +++ b/examples/ui/directional_navigation.rs @@ -214,16 +214,12 @@ fn setup_ui( // but don't loop around when the edge is reached. // While looping is a very reasonable choice, we're not doing it here to demonstrate the different options. for col in 0..N_COLS { - // Don't iterate over the last row, as no lower row exists to connect to - for row in 0..N_ROWS - 1 { - let upper_entity = button_entities.get(&(row, col)).unwrap(); - let lower_entity = button_entities.get(&(row + 1, col)).unwrap(); - directional_nav_map.add_symmetrical_edge( - *upper_entity, - *lower_entity, - CompassOctant::South, - ); - } + let entities_in_column: Vec = (0..N_ROWS) + .map(|row| button_entities.get(&(row, col)).unwrap()) + .copied() + .collect(); + + directional_nav_map.add_edges(&entities_in_column, CompassOctant::South); } // When changing scenes, remember to set an initial focus!