facsimile-wing/scripts/pickups/pickup_behavior_controller.gd

132 lines
4.6 KiB
GDScript

class_name PickupBehaviorController extends Area2D
## Controls visual movement (bob, shake) and drift of a pickup.
## Attach to the root Area2D node of your pickup scene.
@export var behavior: PickupBehavior
@export var drift_behavior: PickupDriftBehavior
var _icon_node: TextureRect
var _spawn_position: Vector2 = Vector2.ZERO
func _ready() -> void:
# Find the runtime-added icon node.
for child in get_children():
if child is TextureRect:
_icon_node = child as TextureRect
break
# Capture the initial spawn position.
_spawn_position = global_position
if behavior != null:
behavior.start()
func _process(delta: float) -> void:
if behavior == null or drift_behavior == null:
return
var alpha: float = behavior.process(delta)
if _icon_node != null:
_icon_node.visible = alpha > 0.01
if behavior != null and behavior.fade_in_duration > 0 and _icon_node is CanvasItem:
_icon_node.modulate.a = alpha
var offset := _compute_animation_offset()
var drift_offset := _compute_drift_offset()
position = _spawn_position + Vector2(drift_offset.x, drift_offset.y)
if _icon_node != null:
_icon_node.position = offset
func _compute_animation_offset() -> Vector2:
# Returns the bob + shake offset applied to the icon node.
var result := Vector2.ZERO
if behavior == null:
return result
# Bob (vertical oscillation).
if behavior.bob_enabled:
var bob_angle := _elapsed_time() * TAU * behavior.bob_frequency
result.y += sin(bob_angle) * behavior.bob_amplitude
# Shake (subtle jitter).
if behavior.shake_enabled:
var shake_angle := _elapsed_time() * TAU * behavior.shake_frequency
result.x += sin(shake_angle) * behavior.shake_amplitude
return result
func _compute_drift_offset() -> Vector2:
# Returns the global drift position offset from initial spawn position.
if drift_behavior == null:
return Vector2.ZERO
match drift_behavior.drift_type:
PickupDriftBehavior.DriftType.STATIONARY:
return Vector2.ZERO
PickupDriftBehavior.DriftType.SINE_WAVE:
var t := _elapsed_time() * drift_behavior.wave_frequency
var travel_speed: float = drift_behavior.drift_speed
if drift_behavior.reverse_direction:
travel_speed *= -1.0
var offset := Vector2.ZERO
if drift_behavior.drift_along_x:
# Travels along X; waves on Y.
offset.x = travel_speed * _elapsed_time()
offset.y = sin(t + drift_behavior.phase_offset) * drift_behavior.wave_amplitude
else:
# Travels along Y; waves on X.
offset.y = travel_speed * _elapsed_time()
offset.x = sin(t + drift_behavior.phase_offset) * drift_behavior.wave_amplitude
return offset
PickupDriftBehavior.DriftType.LEAF_FALL:
var leaf_t := _elapsed_time() * drift_behavior.wave_frequency
var travel_speed: float = drift_behavior.fall_speed
if drift_behavior.reverse_direction:
travel_speed *= -1.0
var offset := Vector2.ZERO
if drift_behavior.drift_along_x:
# Travels along X; waves on Y.
offset.x = travel_speed * _elapsed_time()
offset.y = sin(leaf_t + drift_behavior.phase_offset) * drift_behavior.wave_amplitude
else:
# Travels along Y; waves on X.
offset.y = travel_speed * _elapsed_time()
offset.x = sin(leaf_t + drift_behavior.phase_offset) * drift_behavior.wave_amplitude
return offset
PickupDriftBehavior.DriftType.DIAGONAL:
var dir: float = 1.0 if drift_behavior.diagonal_forward else -1.0
if drift_behavior.reverse_direction:
dir *= -1.0
var offset := Vector2.ZERO
if drift_behavior.drift_along_x:
# Travels along X; secondary on Y.
offset.x = dir * drift_behavior.drift_speed * _elapsed_time()
offset.y = dir * (drift_behavior.drift_speed * 0.5) * _elapsed_time()
else:
# Travels along Y; secondary on X.
offset.y = dir * drift_behavior.drift_speed * _elapsed_time()
offset.x = dir * (drift_behavior.drift_speed * 0.5) * _elapsed_time()
return offset
return Vector2.ZERO
func _elapsed_time() -> float:
if behavior == null:
return 0.0
return behavior._elapsed