Initial commit: Godot Pong game with WebSocket accelerometer control.

Includes platform steering via calibrated accel axes, ball physics,
calibration overlay with axis detection, and runtime tuning sliders.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-28 21:52:35 +02:00
co-authored by Cursor
commit bc25226a31
15 changed files with 808 additions and 0 deletions
+149
View File
@@ -0,0 +1,149 @@
extends Control
signal calibration_finished(calibration: AccelCalibration)
enum Phase { WAITING, RIGHT, LEFT, DONE }
const TRAVEL_PX := 140.0
const PHASE_DURATION := 4.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
@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 start() -> void:
_calibration.reset()
_connection_notified = false
_phase = Phase.WAITING
_phase_time = 0.0
_guide_offset = Vector2.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:
match _phase:
Phase.RIGHT:
_calibration.record_right(accel)
Phase.LEFT:
_calibration.record_left(accel)
func notify_connected() -> void:
if _phase != Phase.WAITING or _connection_notified:
return
_hint_label.text = "Verbunden – tippe „Kalibrierung starten“ oder warte…"
# Automatisch starten sobald verbunden
begin_calibration()
func begin_calibration() -> void:
if _phase == Phase.RIGHT or _phase == Phase.LEFT:
return
_connection_notified = true
_start_button.visible = false
_begin_phase(Phase.RIGHT)
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 t := clampf(_phase_time / PHASE_DURATION, 0.0, 1.0)
var ease := t * t * (3.0 - 2.0 * t)
match _phase:
Phase.RIGHT:
_guide_offset.x = TRAVEL_PX * ease
_instruction = "Neige das Gerät nach rechts – folge dem Kreis →"
if _phase_time >= PHASE_DURATION:
_begin_phase(Phase.LEFT)
Phase.LEFT:
_guide_offset.x = -TRAVEL_PX * ease
_instruction = "Neige das Gerät nach links – folge dem Kreis ←"
if _phase_time >= PHASE_DURATION:
_finish()
_hint_label.text = _instruction
queue_redraw()
func _begin_phase(phase: Phase) -> void:
_phase = phase
_phase_time = 0.0
_guide_offset = Vector2.ZERO
match phase:
Phase.RIGHT:
_instruction = "Neige das Gerät nach rechts – folge dem Kreis →"
Phase.LEFT:
_instruction = "Neige das Gerät nach links – folge dem Kreis ←"
_hint_label.text = _instruction
queue_redraw()
func _finish() -> void:
_phase = Phase.DONE
visible = false
if _calibration.analyze():
calibration_finished.emit(_calibration)
else:
_hint_label.text = "Kalibrierung fehlgeschlagen – zu wenig Daten"
visible = true
_start_button.visible = true
_show_waiting()
func _show_waiting() -> void:
_instruction = "Warte auf Verbindung zu localhost:9090…"
_hint_label.text = _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
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))
var center := _screen_center()
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)