netfishing/ui/controller_focus_navigation.gd

58 lines
1.6 KiB
GDScript3
Raw Permalink Normal View History

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)