1. Einführung
Wir wollten ein kleines Outrun Deluxe in EGSL (Easy Gaming Scripting Language von Markus Mangold) umsetzen. Ziel war es:
- Eine perspektivische Straße
- Spielerauto, Gegnerautos
- Kurven und Drift
- HUD (Zeit, Geschwindigkeit)
- Deko-Elemente wie Palmen
- Hintergrundmusik
EGSL eignet sich gut für einfache 2D-Spiele mit Sprites, Rechtecken und Kreisen. Für perspektivische 3D-Effekte gibt es aber Limitierungen, die wir im Verlauf kennenlernen werden.
2. Grundstruktur
Zunächst legten wir die Spielvariablen und Einstellungen fest:
|
-- Fenstergröße -- Strecke -- Geschwindigkeit -- Spielerzustand -- Farben Himmel -- Arrays -- Sprites |
3. Strecke und Gegner erzeugen
Wir erzeugten die Straße als Tabelle von Segmenten. Jedes Segment enthält:
- curve → Kurvenwert
- y → Höhenprofil
- color → Straßenfarbe
- deco → Zufällige Deko (Palmen)
| function createTrack() for i = 0,799 do local s = {} s.curve = math.sin(i*0.04)*2 + math.sin(i*0.01)*4 s.y = math.sin(i*0.02)*50 s.color = (i % 2 == 0) and {r=70,g=70,b=70} or {r=60,g=60,b=60} s.deco = (math.random(0,4) == 0) table.insert(track, s) end end |
Gegnerautos werden zufällig auf der Strecke verteilt:
| function spawnCars() for i = 1,20 do local c = {} c.z = math.random() * (#track * SEG_LEN) c.x = math.random()*2 - 1 c.speed = MAX_SPEED*0.6 + math.random()*50 c.sprite = enemySprite table.insert(cars, c) end end |
4. Spielerbewegung
Die Spielerbewegung berücksichtigt:
- Geschwindigkeit, Beschleunigung, Bremsen
- Drift basierend auf Kurven und Geschwindigkeit
- Kollisionen mit Gegnerautos
|
function update() return true end -- Geschwindigkeit speed = speed + ACCEL speed = speed - BRAKE speed = speed - DECEL end -- Drift playerX = playerX - DRIFT_FACTOR*(speed/MAX_SPEED) end playerX = playerX + DRIFT_FACTOR*(speed/MAX_SPEED) end -- Position -- Gegnerbewegung c.z = c.z - #track*SEG_LEN end return false |
5. Projektion
Für die pseudo-3D-Perspektive berechnen wir die Position auf dem Bildschirm:
| function project(x,y,z) local scale = CAM_DEPTH / z local sx = (1 + scale*x) * W/2 local sy = (1 - scale*y) * H/2 local sw = scale * ROAD_W * W/2 return sx,sy,sw,scale end |
![]() |
Hinweis: Es gibt eine Falle: Wenn z 0 wird, gab es zuvor Division by zero. Diese muss abgefangen werden, z.B. if z <= 0 then z = 0.1 end. |
6. Rendern der Straße
Hier traten die größten Probleme auf:
- Mit box()/fillbox() konnten wir nur rechteckige Streifen zeichnen.
- Die Straße wurde oft nur als schmale Streifen dargestellt, Grünflächen übermalten sie teilweise.
- Kurven waren kaum sichtbar, manchmal wirkte alles grau.
- Das liegt an Limitierungen von EGSL: Es gibt keine Polygon-/Dreiecksfunktionen (kein filltriangle()), nur Rechtecke und Linien.
Beispielhafte Pseudo-Perspektive (noch stark vereinfacht):
|
function render() local base = math.floor(pos/SEG_LEN) for n = 0,DRAW_DIST do z1 = 0.1 end local sx,sy,sw,_ = project((playerX-x)*ROAD_W, camH-seg.y, z1) -- Straße
dx = dx + seg.curve |
![]() |
Probleme:
|
7. HUD, Gegner, Spieler und Deko
Nachdem die Projektion der Straße berechnet ist, werden die Overlay-Elemente—Gegnerautos, Spielerauto, Dekorationen und HUD—darüber gezeichnet.
Gegnerautos
- Werden auf den berechneten Positionen mit putimage() gezeichnet:
|
for _, c in ipairs(cars) do
local dz = c.z - pos if dz < 0 then dz = dz + #track*SEG_LEN
end
if dz > 0 and dz < DRAW_DIST*SEG_LEN then local sx, sy, sw, scale = project(c.x*ROAD_W, 100, dz) putimage(sx, sy-20, enemySprite) end end |
- Bewegung: Gegner fahren entlang der Strecke und „wrappen“, wenn sie das Ende erreichen.
- Kollision: Verringert die Geschwindigkeit des Spielers, wenn er nahe an einem Gegner ist.
Spielerauto
- Wird immer unten in der Bildschirmmitte gezeichnet:
putimage(W/2, H-60, playerSprite)
|
Dekorationen (Palmen)
- Werden auf bestimmten Streckensegmenten (seg.deco) zufällig platziert:
| if seg.deco and n % 10 == 0 then putimage(sx + sw*1.2, sy - 50, palmSprite) putimage(sx - sw*1.5, sy - 50, palmSprite) end |
HUD (Zeit und Geschwindigkeit)
- Wird mit bmptext() und einer Bitmap-Schrift dargestellt:
| bmptext(20, 20, "TIME: "..math.floor(lapTime), myfont) bmptext(20, 50, "SPEED: "..math.floor(speed), myfont) |
Zusammenfassung:- Diese Elemente werden 2D über der Straße gezeichnet.
- In EGSL lassen sie sich relativ einfach umsetzen.
- Die größte visuelle Einschränkung bleibt die pseudo-3D-Straße, aber Gegner, Spieler, Dekorationen und HUD funktionieren korrekt.
8. Musik
| function initMusic() gamesound = loadsound("Blaster.wav") playsound(gamesound,16,2,true) end |
9. Main-Loop
|
function main() createTrack() local gameover = false break end closewindow() main() |
10. Fazit: EGSL für Outrun?
- Vorteile von EGSL: Einfach, gut für 2D-Sprites, Rechtecke, Linien, einfache Spiele.
- Probleme bei perspektivischer Straße:
- Keine Polygon- oder Dreiecks-Funktionen (filltriangle())
- Rechtecke können keine echte perspektivische Straße erzeugen
- Kurven, Grünflächen und Streifen überlappen, Straße wirkt „schmal und flach“
- Division by Zero muss vorsichtig abgefangen werden
- Realistische Outrun-Optik mit Pseudo-3D ist in EGSL nicht möglich
Schlussfolgerung:
EGSL ist für ein echtes Outrun Deluxe mit perspektivischer Straße leider nicht geeignet.
Wer die Straße wirklich perspektivisch darstellen möchte, muss eine Engine mit Polygon-/Dreiecksfunktionen verwenden, z. B. LOVE2D, Unity oder Godot.

