Aistea Kingdoms – wie ein Browserspiel mit Vanilla JS und Three.js entsteht
Frontend & 3D

Aistea Kingdoms – wie ein Browserspiel mit Vanilla JS und Three.js entsteht

Yannick Aister 4 Min. Lesezeit 96 Aufrufe

Ein Spiel, das ich gebaut habe – und was drin steckt

Aistea Kingdoms ist ein browserbasieres 3D-Strategiespiel: Burg bauen, Belagerungswellen abwehren, Highscore knacken. Kein App-Store, keine Installation – direkt im Browser, auch auf dem Handy. Was sich hinter diesem Projekt technisch verbirgt, ist vielleicht überraschend schlank.

Framework? Nein danke – Vanilla JS mit Vite

Das gesamte Spiel läuft auf reinem, modularem JavaScript (ES-Module) – kein React, kein Vue, kein Angular. Das hält die Ladezeiten kurz und gibt mir volle Kontrolle über jede Interaktion. Als Build-Tool kommt Vite in einem Multi-Page-Setup zum Einsatz: vier eigenständige Einstiegspunkte – Landingpage, Spiel, Profilseite und Admin-Bereich – je ein eigenes HTML-Entry.

Kein Framework-Overhead, keine Abstraktionsebenen, die man erst verstehen muss. Was im Code steht, passiert im Browser – direkt und nachvollziehbar.

Three.js – die 3D-Welt im Browser

Das visuelle Herzstück ist Three.js. Es rendert die Burg, die Landschaft, Truppen und Kampfeffekte direkt im Browser via WebGL – ganz ohne Plugin oder App-Installation. Der Stil ist bewusst Low-Poly: Das hält die Szene performant genug fürs mobile Web, ohne auf Atmosphäre zu verzichten.

Für alle, die Three.js noch nicht kennen: Es ist eine JavaScript-Bibliothek, die die rohe WebGL-API abstrahiert und 3D-Szenen, Kameras, Materialien und Animationen auf eine handhabbare Art zugänglich macht.

Der Burg-Builder: Raycasting + Grid

Gebäude platzieren läuft über klassisches Raycasting: Ein THREE.Raycaster schießt vom Mauszeiger aus einen Strahl in die 3D-Szene, der auf eine unsichtbare Bodenebene trifft. Der Treffpunkt wird auf ein 2×2-Einheiten-Grid gerundet:

 

Math.round(x / 2) * 2

 

So entsteht das saubere Kachelraster, auf dem gebaut wird. Während man ein Gebäude ausgewählt hat, folgt ein halbtransparentes Ghost-Mesh der Maus – und färbt sich live grün oder rot, je nachdem ob der Platz gültig ist (Kollision mit anderen Gebäuden, zu wenig Ressourcen, Truppen im Weg).

Mauern lassen sich per Klick-Halten-Ziehen als ganze Reihe auf einmal setzen. Waffen wie Balliste oder Kanone lassen sich direkt auf bestehende Mauersegmente aufsetzen – dafür checkt derselbe Raycast, ob unter dem Cursor eine Mauer statt freiem Boden liegt.

Belagerungswellen: State Machine + A*-Pathfinding

Zwei Ebenen steuern, wie ein Angriff abläuft:

Ebene 1 – Zustandsmaschine: Eine simple State Machine auf oberster Ebene regelt den Spielfluss: build → battle → nächste Runde, plus die Endzustände won / lost. Sie entscheidet, wann gebaut werden darf und wann die Verteidigung automatisch feuert.

Ebene 2 – A*-Pathfinding: Für die Gegner-Bewegung läuft ein A*-Algorithmus mit eigener Prioritätswarteschlange (Min-Heap) über dasselbe Grid, das auch der Builder nutzt. Gegner suchen sich den kürzesten Weg zur Burg – umgehen Mauern, zielen aber gezielt auf Tore, weil diese als Schwachstelle gelten.

Wird eine Mauer zerstört oder neu gebaut, erhöht sich ein interner Navigations-Versions-Zähler. Das macht alle betroffenen gecachten Routen ungültig – Gegner planen ihren Pfad dann im laufenden Kampf neu, statt stur einem alten Weg zu folgen. Angriffe selbst laufen über ein einfaches Windup/Cooldown-Timing pro Einheit (kurze Vorbereitung vor dem Treffer, dann Abklingzeit).

Backend: PHP + MySQL, kein Framework

Auf der Serverseite läuft ein bewusst schlankes PHP-Backend ohne Framework (kein Laravel, kein Symfony) gegen eine MySQL/MariaDB-Datenbank. Es kümmert sich um Nutzerkonten, Session-Auth und die weltweite Bestenliste.

Für Gäste und Offline-Fälle gibt es zusätzlich einen localStorage-Fallback – man kann also auch ganz ohne Account spielen. Bei Login wird synchronisiert.

Diese Schlankheit macht das Hosting günstig und wartungsarm: Ein klassisches Shared-Webspace-Setup reicht aus.

Tests & Deployment

Die Spiellogik wird mit dem eingebauten Node-Test-Runner abgesichert, echte Browser-Flows (Login, Auth) mit Playwright. Deployt wird unspektakulär, aber zuverlässig: Ein Skript baut das Projekt und synchronisiert es per rsync/SSH auf den Webspace – keine Cloud-Infrastruktur, kein Kubernetes, kein Overhead.

Was mich überrascht hat: die CSS-Kaskade

Ehrlich gesagt war der härteste Gegner nicht das Pathfinding, sondern die eigene CSS-Datei. Über viele Entwicklungs-Sessions ist alles ans Ende einer einzigen Datei angehängt worden – dicht gepackt, ohne Struktur. Das Ergebnis: Eine neue Mobile-Regel war technisch korrekt, wurde aber von einer viel früher stehenden, unbedingten Regel mit gleicher Spezifität wieder überschrieben.

Bei CSS gewinnt bei gleicher Spezifität immer, was weiter unten in der Datei steht – Media Query hin oder her.

Das hat mich ein paarmal auf die falsche Fährte geführt, bis ich mir ein kleines Skript geschrieben habe, das die tatsächliche Kaskaden-Reihenfolge nachrechnet. Lehrreicher Moment.

Die Philosophie dahinter

So wenig Abhängigkeiten wie möglich, so viel direkte Kontrolle wie möglich – ein Stack, der genau das tut, was das Spiel braucht, und nicht mehr. Kein Framework, das man erst verstehen muss. Kein Cloud-Setup, das Geld kostet bevor der erste Spieler da ist.

Das Ergebnis: Ein 3D-Strategiespiel, das direkt im Browser läuft – auf dem Desktop genauso wie auf dem Handy. Selbst ausprobieren.

Teilen

Kommentar hinterlassen