Cozy Train
Low-Poly-Zugspiel in Godot 4.7. Eine Crew verwaltet gemeinsam einen Zug: einer fährt, die anderen kontrollieren Tickets, servieren Getränke und laden Güter um. First-Person, entspannter Ton, alles aus Primitiven gebaut.
Starten
~/bin/godot --path . # Spiel
~/bin/godot --editor --path . # Editor
./tests/run.sh # Tests (Spiellogik, Eingabe, Netzwerk)
Im Menü: Einzelspieler, Sitzung hosten oder Beitreten mit Adresse und Port (Standard 7654). Der Host simuliert alles, Clients schicken Wünsche und bekommen den Zustand zurück.
Die Eingabe-Tests brauchen ein Fenster, die anderen laufen headless.
Sie schieben echte Input-Events durch die Godot-Kette (GUI -> _unhandled_input)
und prüfen die Wirkung im Spiel, nicht nur den Aufruf. direction_test.gd
vergleicht jede Laufrichtung mit den Achsen der Kamera – ein Vorzeichenfehler
beim Seitwärtsgehen fällt damit sofort auf.
Steuerung
| Taste | Wirkung |
|---|---|
| Maus | Umsehen |
| W A S D | Laufen (im Führerstand: W Gas, S Bremse) |
| Shift | Rennen |
| E | Benutzen; am Güterwagen halten |
| H | Pfeife (nur wer fährt) |
| M | Musik an/aus |
| V | Außenkamera (A/D drehen, R/F neigen, Rad zoomen) |
| Esc | Maus freigeben / Terminal schließen |
Was es zu tun gibt
Fahren. Im Führerstand am Pult E drücken. Nur ein Spieler kann fahren; E gibt die Steuerung wieder frei. Am Bahnsteig anhalten – der Zug gilt als angekommen, wenn seine Mitte nah genug am Bahnsteig steht.
Fahrgäste. Sie warten am Bahnsteig, steigen beim Halt ein und wollen kontrolliert werden. Ein Blick auf den Gast, E – das Ticket bringt sofort Geld. Wer bis zum Ziel unkontrolliert bleibt, fährt schwarz und kostet Ansehen.
Servieren. Mit einem Speisewagen bekommen Gäste nach einer Weile Durst. Getränk an der Theke holen (E), zum Gast, E. Bringt Trinkgeld und hält die Laune oben. Schlechte Laune kostet beim Aussteigen Ansehen.
Güter. Im Güterwagen steht eine Ladekonsole. Im Halt E gedrückt halten: Erst wird abgeliefert, was der Bahnhof annimmt, danach geladen, was er produziert.
| Bahnhof | produziert | nimmt an |
|---|---|---|
| Waldkirch | Holz | Getreide |
| Kohlberg | Kohle | – |
| Eisenwerk | – | Kohle, Holz |
| Grüntal | Getreide | – |
| Ahornstadt | – | Getreide, Holz |
Depot-Terminal. Hinten im Führerstand. Dort kaufst und verkaufst du Waggons, tauschst die Lok und schaltest Ausbauten frei. Bessere Loks und Waggons brauchen Ansehen – das steigt mit zufriedenen Fahrgästen und gelieferten Waren.
Aufbau
Die Welt entsteht komplett zur Laufzeit, scenes/main.tscn ist nur der
Einstiegspunkt.
| Datei | Inhalt |
|---|---|
shaders/flat.gdshader |
Facettierte Optik über Screen-Space-Normalen |
scripts/game.gd |
Weltaufbau, Spielstand, Aktionen, Netzwerk |
scripts/car.gd |
Ein Fahrzeug: Hülle, Innenraum, Sitze, Hotspots |
scripts/car_defs.gd |
Stammdaten für Loks, Waggons, Ausbauten |
scripts/train.gd |
Verband, Fahrphysik, Zug- zu Weltkoordinaten |
scripts/player.gd |
First-Person-Bewegung in Zugkoordinaten |
scripts/passenger.gd |
Fahrgast mit Ticket, Wunsch und Laune |
scripts/station.gd |
Bahnsteig, Lager, Warteplätze |
scripts/world.gd |
Gelände und Vegetation (MultiMesh) |
scripts/track.gd |
Gleiskurve und Gleisoptik |
scripts/shop.gd, hud.gd, menu.gd |
Oberflächen |
scripts/camera_rig.gd |
Außenkamera |
scripts/synth.gd |
Wavetable-Synth: Töne, Rauschen, Hüllkurven |
scripts/sfx.gd |
Alle Klänge und das Musikstück als Rezept |
scripts/audio.gd |
Wiedergabe, Fahrgeräusch, Radschläge, Schritte |
Zugkoordinaten
Spieler bewegen sich nicht in Weltkoordinaten, sondern in einer
Längsposition s entlang des Zuges plus einer Querposition x.
train.locate(s) liefert den passenden Waggon, dessen Transform die
Weltposition ergibt. Dadurch bleibt der Innenraum auch bei voller Fahrt
und in Kurven stabil begehbar, ohne Physik-Plattformen.
Klang
Es gibt keine Audiodateien. sfx.gd beschreibt jeden Klang als Rezept aus
Sinustönen, Rauschen und Hüllkurven; synth.gd rechnet daraus PCM. Das
läuft beim Start in einem Thread (rund drei Sekunden), bis dahin bleibt es
still – das Spiel ruckelt dabei nicht.
Die Musik ist ein 27-Sekunden-Stück in 72 bpm: Flächen über Am7–Fmaj7– Cmaj7–G6, dazu Bass, sparsame Glockentöne und ein leiser Shaker. Der Ausklang wird auf den Anfang gemischt, dadurch ist die Schleife nahtlos.
Das Fahrgeräusch ist eine Rauschschleife, deren Lautstärke und Tonhöhe am Tempo hängen. Die Radschläge kommen nicht per Timer, sondern alle 8,5 gefahrene Meter – deshalb passen sie automatisch zur Geschwindigkeit.
Netzwerk
Godot-Hochsprachen-RPCs über ENet. Der Host ist maßgeblich für Zug, Fahrgäste, Lager und Kasse. Clients senden ihre Pose (15 Hz) und Aktionswünsche; der Host verteilt Zustandspakete (4 Hz). Die Welt wird auf allen Rechnern aus demselben Seed erzeugt und muss deshalb nicht übertragen werden.