5757//! - **Precise control**: Define exact navigation flow, including non-obvious connections like looping edges
5858//! - **Cross-layer navigation**: Connect elements across different UI layers or z-index levels
5959//! - **Custom behavior**: Implement domain-specific navigation patterns (e.g., spreadsheet-style wrapping)
60+
6061use alloc:: vec:: Vec ;
6162use bevy_app:: prelude:: * ;
6263use bevy_camera:: visibility:: InheritedVisibility ;
@@ -407,7 +408,7 @@ impl DirectionalNavigationMap {
407408pub struct DirectionalNavigation < ' w , ' s > {
408409 /// The currently focused entity.
409410 pub focus : ResMut < ' w , InputFocus > ,
410- /// The manual override navigation map containing pre-specified connections between entities.
411+ /// The directional navigation map containing manually defined connections between entities.
411412 pub map : Res < ' w , DirectionalNavigationMap > ,
412413 /// Configuration for the automated portion of the navigation algorithm.
413414 pub config : Res < ' w , AutoNavigationConfig > ,
@@ -449,7 +450,12 @@ impl<'w, 's> DirectionalNavigation<'w, 's> {
449450 self . focus . set ( new_focus) ;
450451 Ok ( new_focus)
451452 } else if let Some ( origin) = self . entity_to_focusable_area ( current_focus)
452- && let Some ( new_focus) = self . find_best_candidate ( origin, direction)
453+ && let Some ( new_focus) = find_best_candidate (
454+ & origin,
455+ direction,
456+ & self . get_navigable_nodes ( ) ,
457+ & self . config ,
458+ )
453459 {
454460 self . focus . set ( new_focus) ;
455461 Ok ( new_focus)
@@ -464,44 +470,7 @@ impl<'w, 's> DirectionalNavigation<'w, 's> {
464470 }
465471 }
466472
467- /// Finds the best entity to navigate to from the origin in the given direction.
468- /// For details on what "best" means here, refer to [`AutoNavigationConfig`].
469- fn find_best_candidate (
470- & self ,
471- origin : FocusableArea ,
472- direction : CompassOctant ,
473- ) -> Option < Entity > {
474- // Find best candidate in this direction
475- let mut best_candidate = None ;
476- let mut best_score = f32:: INFINITY ;
477-
478- for candidate in self . get_navigable_nodes ( ) {
479- // Skip self
480- if candidate. entity == origin. entity {
481- continue ;
482- }
483-
484- // Score the candidate
485- let score = score_candidate (
486- origin. position ,
487- origin. size ,
488- candidate. position ,
489- candidate. size ,
490- direction,
491- & self . config ,
492- ) ;
493-
494- if score < best_score {
495- best_score = score;
496- best_candidate = Some ( candidate. entity ) ;
497- }
498- }
499-
500- best_candidate
501- }
502-
503- /// Queries for all visible [`FocusableArea`] that are eligible to be automatically navigated to.
504- /// These are nodes with
473+ /// Returns a vec of [`FocusableArea`] representing nodes that are eligible to be automatically navigated to.
505474 fn get_navigable_nodes ( & self ) -> Vec < FocusableArea > {
506475 self . navigable_entities_query
507476 . iter ( )
@@ -522,6 +491,7 @@ impl<'w, 's> DirectionalNavigation<'w, 's> {
522491 }
523492
524493 /// Gets the [`FocusableArea`] of the provided entity, if it exists.
494+ ///
525495 /// Returns None if there was a [`QueryEntityError`](bevy_ecs::query::QueryEntityError).
526496 fn entity_to_focusable_area ( & self , entity : Entity ) -> Option < FocusableArea > {
527497 self . focusable_area_query
@@ -706,6 +676,44 @@ fn score_candidate(
706676 distance + alignment_penalty
707677}
708678
679+ /// Finds the best entity to navigate to from the origin towards the given direction.
680+ ///
681+ /// For details on what "best" means here, refer to [`AutoNavigationConfig`].
682+ fn find_best_candidate (
683+ origin : & FocusableArea ,
684+ direction : CompassOctant ,
685+ candidates : & [ FocusableArea ] ,
686+ config : & AutoNavigationConfig ,
687+ ) -> Option < Entity > {
688+ // Find best candidate in this direction
689+ let mut best_candidate = None ;
690+ let mut best_score = f32:: INFINITY ;
691+
692+ for candidate in candidates {
693+ // Skip self
694+ if candidate. entity == origin. entity {
695+ continue ;
696+ }
697+
698+ // Score the candidate
699+ let score = score_candidate (
700+ origin. position ,
701+ origin. size ,
702+ candidate. position ,
703+ candidate. size ,
704+ direction,
705+ config,
706+ ) ;
707+
708+ if score < best_score {
709+ best_score = score;
710+ best_candidate = Some ( candidate. entity ) ;
711+ }
712+ }
713+
714+ best_candidate
715+ }
716+
709717/// Automatically generates directional navigation edges for a collection of nodes.
710718///
711719/// This function takes a slice of navigation nodes with their positions and sizes, and populates
@@ -761,30 +769,7 @@ pub fn auto_generate_navigation_edges(
761769 }
762770
763771 // Find best candidate in this direction
764- let mut best_candidate = None ;
765- let mut best_score = f32:: INFINITY ;
766-
767- for candidate in nodes {
768- // Skip self
769- if candidate. entity == origin. entity {
770- continue ;
771- }
772-
773- // Score the candidate
774- let score = score_candidate (
775- origin. position ,
776- origin. size ,
777- candidate. position ,
778- candidate. size ,
779- octant,
780- config,
781- ) ;
782-
783- if score < best_score {
784- best_score = score;
785- best_candidate = Some ( candidate. entity ) ;
786- }
787- }
772+ let best_candidate = find_best_candidate ( origin, octant, nodes, config) ;
788773
789774 // Add edge if we found a valid candidate
790775 if let Some ( neighbor) = best_candidate {
0 commit comments