demo-game/scripts/calibration_overlay.gd
simon a32a223881 Add split-screen Space Shooter with pod steering and boost.
Two-player mode with client assignment, 3D vector calibration for cylindrical
pods, lives, tap/double-tap boost with low-intensity LED cooldown feedback,
and Kenney sprite assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-06 18:56:55 +02:00

227 lines
6.0 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends Control
signal calibration_finished(calibration: AccelCalibration)
enum Phase { WAITING, NEUTRAL, RIGHT, LEFT, DONE }
const TRAVEL_PX := 140.0
const NEUTRAL_DURATION := 3.5
const SWAY_DURATION := 5.0
const SAMPLE_START_FRACTION := 0.5
const CIRCLE_RADIUS := 44.0
const NEEDLE_LEN := 58.0
var _phase := Phase.WAITING
var _phase_time := 0.0
var _guide_offset := Vector2.ZERO
var _instruction := ""
var _calibration := AccelCalibration.new()
var _connection_notified := false
var _context_label := ""
var _last_accel := Vector3i.ZERO
@onready var _hint_label: Label = $HintLabel
@onready var _start_button: Button = $StartButton
func _ready() -> void:
set_anchors_preset(Control.PRESET_FULL_RECT)
mouse_filter = Control.MOUSE_FILTER_PASS
_start_button.pressed.connect(_on_start_pressed)
_show_waiting()
func set_context_label(text: String) -> void:
_context_label = text
if _phase == Phase.WAITING:
_show_waiting()
func start() -> void:
_calibration.reset()
_connection_notified = false
_phase = Phase.WAITING
_phase_time = 0.0
_guide_offset = Vector2.ZERO
_last_accel = Vector3i.ZERO
visible = true
_start_button.visible = true
_show_waiting()
queue_redraw()
func is_active() -> bool:
return _phase != Phase.DONE
func feed_accel(accel: Vector3i) -> void:
_last_accel = accel
if not _should_record():
return
match _phase:
Phase.NEUTRAL:
_calibration.record_neutral(accel)
Phase.RIGHT:
_calibration.record_right(accel)
Phase.LEFT:
_calibration.record_left(accel)
func get_live_projection() -> float:
if _calibration.uses_vector:
return _calibration.project(_last_accel) - _calibration.center
return 0.0
func notify_connected() -> void:
if _phase != Phase.WAITING or _connection_notified:
return
_hint_label.text = "Verbunden tippe „Kalibrierung starten“ oder warte…"
begin_calibration()
func begin_calibration() -> void:
if _phase == Phase.NEUTRAL or _phase == Phase.RIGHT or _phase == Phase.LEFT:
return
_connection_notified = true
_start_button.visible = false
_begin_phase(Phase.NEUTRAL)
func _on_start_pressed() -> void:
begin_calibration()
func _process(delta: float) -> void:
if _phase == Phase.WAITING or _phase == Phase.DONE:
return
_phase_time += delta
var duration := _phase_duration()
var t := clampf(_phase_time / duration, 0.0, 1.0)
var ease := t * t * (3.0 - 2.0 * t)
match _phase:
Phase.NEUTRAL:
_guide_offset = Vector2.ZERO
_instruction = _phase_instruction(
"Halte den Zylinder senkrecht und ruhig\n(mittlere Position nicht schwanken)"
)
if _phase_time >= duration:
_begin_phase(Phase.RIGHT)
Phase.RIGHT:
_guide_offset.x = TRAVEL_PX * ease
_instruction = _phase_instruction(
"Schwank den Pod langsam nach rechts ↷\nWie ein Pendel Spitze kippt nach rechts"
)
if _phase_time >= duration:
_begin_phase(Phase.LEFT)
Phase.LEFT:
_guide_offset.x = -TRAVEL_PX * ease
_instruction = _phase_instruction(
"Schwank den Pod langsam nach links ↶\nWie ein Pendel Spitze kippt nach links"
)
if _phase_time >= duration:
_finish()
var record_hint := ""
if _phase != Phase.NEUTRAL and _phase_time < duration * SAMPLE_START_FRACTION:
record_hint = "\n\n… halte gleich die Endposition"
elif _phase != Phase.WAITING and _phase != Phase.DONE:
record_hint = "\n\n✓ Aufnahme läuft"
_hint_label.text = _instruction + record_hint
queue_redraw()
func _phase_duration() -> float:
match _phase:
Phase.NEUTRAL:
return NEUTRAL_DURATION
return SWAY_DURATION
func _should_record() -> bool:
if _phase == Phase.WAITING or _phase == Phase.DONE:
return false
return _phase_time >= _phase_duration() * SAMPLE_START_FRACTION
func _begin_phase(phase: Phase) -> void:
_phase = phase
_phase_time = 0.0
_guide_offset = Vector2.ZERO
match phase:
Phase.NEUTRAL:
_instruction = _phase_instruction(
"Halte den Zylinder senkrecht und ruhig\n(mittlere Position nicht schwanken)"
)
Phase.RIGHT:
_instruction = _phase_instruction(
"Schwank den Pod langsam nach rechts ↷\nWie ein Pendel Spitze kippt nach rechts"
)
Phase.LEFT:
_instruction = _phase_instruction(
"Schwank den Pod langsam nach links ↶\nWie ein Pendel Spitze kippt nach links"
)
_hint_label.text = _instruction
queue_redraw()
func _finish() -> void:
_phase = Phase.DONE
visible = false
if _calibration.analyze():
calibration_finished.emit(_calibration.duplicate())
else:
_hint_label.text = (
"Kalibrierung fehlgeschlagen.\n"
+ "Schwank weiter links/rechts oder halte die Neutralposition länger still."
)
visible = true
_start_button.visible = true
_show_waiting()
func _phase_instruction(base: String) -> String:
if _context_label == "":
return base
return "%s\n\n%s" % [_context_label, base]
func _show_waiting() -> void:
_instruction = "Warte auf Verbindung zu localhost:8081…"
var prefix := "%s\n\n" % _context_label if _context_label != "" else ""
_hint_label.text = prefix + _instruction + "\n(Oder „Kalibrierung starten“ ohne Verbindung)"
func _screen_center() -> Vector2:
return get_viewport_rect().size * 0.5
func _draw() -> void:
if _phase == Phase.DONE:
return
var pos := _screen_center() + _guide_offset
var center := _screen_center()
if _phase == Phase.NEUTRAL:
var pulse := 0.85 + 0.15 * sin(_phase_time * 4.0)
draw_circle(center, CIRCLE_RADIUS * pulse, Color(0.35, 0.85, 0.55, 0.18))
draw_arc(center, CIRCLE_RADIUS * pulse, 0.0, TAU, 64, Color(0.45, 0.95, 0.65), 2.5)
draw_line(center + Vector2(-18, 0), center + Vector2(18, 0), Color(1, 1, 1, 0.35), 2.0)
return
draw_circle(pos, CIRCLE_RADIUS, Color(0.25, 0.72, 0.95, 0.2))
draw_arc(pos, CIRCLE_RADIUS, 0.0, TAU, 64, Color(0.35, 0.82, 1.0), 2.5)
var needle_tip := pos + Vector2(0.0, -NEEDLE_LEN)
draw_line(pos, needle_tip, Color(1.0, 1.0, 1.0, 0.95), 3.0)
draw_circle(needle_tip, 5.0, Color(1.0, 1.0, 1.0))
if _phase == Phase.RIGHT:
draw_line(center, center + Vector2(TRAVEL_PX, 0.0), Color(1.0, 1.0, 1.0, 0.12), 1.0)
elif _phase == Phase.LEFT:
draw_line(center, center + Vector2(-TRAVEL_PX, 0.0), Color(1.0, 1.0, 1.0, 0.12), 1.0)