netfishing/ui/components/bubble_menu/bubble_cluster.gd

115 lines
3.2 KiB
GDScript3
Raw Normal View History

class_name BubbleCluster
extends Control
const ControllerFocusNavigationType = preload(
"res://ui/controller_focus_navigation.gd"
)
@export var profile: BubbleMenuProfile
@export var desktop_reference_size: Vector2 = Vector2(396.0, 318.0)
@export var compact_reference_size: Vector2 = Vector2(294.0, 200.0)
@export_range(0.0, 1.0, 0.01) var motion_scale: float = 1.0
var _bubbles: Array[BubbleButton] = []
var _elapsed: float = 0.0
var _layout_scale: float = 1.0
func configure(bubbles: Array[BubbleButton]) -> void:
_bubbles = bubbles
for index: int in _bubbles.size():
var bubble: BubbleButton = _bubbles[index]
if bubble.profile == null:
bubble.profile = profile
bubble.apply_profile()
func apply_layout(field_size: Vector2, compact: bool) -> void:
_layout_scale = minf(
field_size.x / desktop_reference_size.x,
field_size.y / desktop_reference_size.y
)
var authored_extent: Vector2 = desktop_reference_size * _layout_scale
var layout_origin := Vector2(
(field_size.x - authored_extent.x) * 0.5,
(field_size.y - authored_extent.y) * 0.5
)
for bubble: BubbleButton in _bubbles:
var bubble_size: Vector2 = bubble.get_layout_size(
_layout_scale,
compact
)
var center: Vector2 = (
layout_origin
+ bubble.get_authored_anchor(compact) * _layout_scale
)
bubble.apply_layout(
center,
bubble_size,
profile.font_size_ratio
)
ControllerFocusNavigationType.configure_spatial_neighbors(_bubbles)
func advance_motion(delta: float) -> void:
_elapsed = fmod(_elapsed + delta, 120.0)
var targets: Array[Vector2] = []
var visual_radii: Array[float] = []
var visual_scales: Array[Vector2] = []
for bubble: BubbleButton in _bubbles:
bubble.advance_emphasis(delta)
var target: Vector2 = bubble.calculate_target(
_elapsed,
_layout_scale,
motion_scale
)
var visual_scale: Vector2 = bubble.calculate_visual_scale(
_elapsed,
motion_scale
)
targets.append(target)
visual_scales.append(visual_scale)
visual_radii.append(bubble.get_visual_radius(visual_scale))
for first_index: int in _bubbles.size():
for second_index: int in range(first_index + 1, _bubbles.size()):
var first_center: Vector2 = (
targets[first_index]
+ _bubbles[first_index].presented_size * 0.5
)
var second_center: Vector2 = (
targets[second_index]
+ _bubbles[second_index].presented_size * 0.5
)
var center_delta: Vector2 = second_center - first_center
var distance: float = center_delta.length()
var desired_distance: float = (
visual_radii[first_index]
+ visual_radii[second_index]
+ profile.contact_gap * _layout_scale
)
if distance >= desired_distance:
continue
var direction := (
center_delta / distance
if distance > 0.001
else Vector2.RIGHT.rotated(float(first_index + 1))
)
var correction: Vector2 = (
direction
* minf(
(desired_distance - distance) * 0.5,
profile.maximum_separation * _layout_scale
)
)
targets[first_index] -= correction
targets[second_index] += correction
var position_weight: float = 1.0 - exp(
-profile.position_response * delta
)
for index: int in _bubbles.size():
_bubbles[index].apply_presentation(
targets[index],
visual_scales[index],
position_weight
)