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 <robparrett@gmail.com>
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
This commit is contained in:
Alice Cecile 2025-01-15 18:43:03 -05:00 committed by GitHub
parent 5cc3f4727e
commit 72f70745c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 11 deletions

View File

@ -173,7 +173,16 @@ impl DirectionalNavigationMap {
self.add_edge(b, a, direction.opposite()); 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. /// 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) { 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); 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] #[test]
fn looping_edges() { fn looping_edges() {
let mut world = World::new(); let mut world = World::new();

View File

@ -214,16 +214,12 @@ fn setup_ui(
// but don't loop around when the edge is reached. // 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. // While looping is a very reasonable choice, we're not doing it here to demonstrate the different options.
for col in 0..N_COLS { for col in 0..N_COLS {
// Don't iterate over the last row, as no lower row exists to connect to let entities_in_column: Vec<Entity> = (0..N_ROWS)
for row in 0..N_ROWS - 1 { .map(|row| button_entities.get(&(row, col)).unwrap())
let upper_entity = button_entities.get(&(row, col)).unwrap(); .copied()
let lower_entity = button_entities.get(&(row + 1, col)).unwrap(); .collect();
directional_nav_map.add_symmetrical_edge(
*upper_entity, directional_nav_map.add_edges(&entities_in_column, CompassOctant::South);
*lower_entity,
CompassOctant::South,
);
}
} }
// When changing scenes, remember to set an initial focus! // When changing scenes, remember to set an initial focus!