feat: complete controller-first menu navigation
This commit is contained in:
parent
26c9308fcf
commit
4ac7d5edb8
35 changed files with 2013 additions and 85 deletions
57
ui/controller_focus_navigation.gd
Normal file
57
ui/controller_focus_navigation.gd
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
class_name ControllerFocusNavigation
|
||||
extends RefCounted
|
||||
|
||||
const PERPENDICULAR_WEIGHT: float = 2.5
|
||||
const MINIMUM_FORWARD_DISTANCE: float = 0.5
|
||||
|
||||
|
||||
static func configure_spatial_neighbors(controls: Array) -> void:
|
||||
var candidates: Array[Control] = []
|
||||
for item: Variant in controls:
|
||||
var control := item as Control
|
||||
if control == null or not control.is_visible_in_tree():
|
||||
continue
|
||||
candidates.append(control)
|
||||
for control: Control in candidates:
|
||||
control.focus_neighbor_left = _neighbor_path(
|
||||
control, candidates, Vector2.LEFT
|
||||
)
|
||||
control.focus_neighbor_right = _neighbor_path(
|
||||
control, candidates, Vector2.RIGHT
|
||||
)
|
||||
control.focus_neighbor_top = _neighbor_path(
|
||||
control, candidates, Vector2.UP
|
||||
)
|
||||
control.focus_neighbor_bottom = _neighbor_path(
|
||||
control, candidates, Vector2.DOWN
|
||||
)
|
||||
|
||||
|
||||
static func _neighbor_path(
|
||||
origin: Control,
|
||||
candidates: Array[Control],
|
||||
direction: Vector2,
|
||||
) -> NodePath:
|
||||
var origin_center: Vector2 = origin.get_global_rect().get_center()
|
||||
var best: Control = null
|
||||
var best_score: float = INF
|
||||
for candidate: Control in candidates:
|
||||
if candidate == origin or candidate.focus_mode == Control.FOCUS_NONE:
|
||||
continue
|
||||
var delta: Vector2 = (
|
||||
candidate.get_global_rect().get_center() - origin_center
|
||||
)
|
||||
var forward_distance: float = delta.dot(direction)
|
||||
if forward_distance <= MINIMUM_FORWARD_DISTANCE:
|
||||
continue
|
||||
var perpendicular_distance: float = absf(delta.cross(direction))
|
||||
var score: float = (
|
||||
forward_distance
|
||||
+ perpendicular_distance * PERPENDICULAR_WEIGHT
|
||||
)
|
||||
if score < best_score:
|
||||
best = candidate
|
||||
best_score = score
|
||||
if best == null:
|
||||
return NodePath()
|
||||
return origin.get_path_to(best)
|
||||
Loading…
Add table
Add a link
Reference in a new issue