Wie haben Spielefirmen Spiele "handgestrickt", bevor es ausgereifte Engines gab?


Die Antwort auf diese Frage ist weitaus roher, als die meisten Menschen sich vorstellen.

Heutige Spieleentwickler öffnen Unity oder Unreal, und der Editor ist da, die Physik-Engine ist da, die Rendering-Pipeline ist da, das Beleuchtungssystem ist da. Ihre Aufgabe ist es, Inhalte in ein bereits gebautes Framework einzufügen. Das gab es in den 1980er Jahren schlichtweg nicht. Es gab kein Framework, keine Engine, keine wiederverwendbare Infrastruktur. Wenn du ein Spiel machen wolltest, musstest du zuerst eine Maschine bauen, die es ausführen konnte, und dann auf dieser Maschine in Assemblersprache jedem einzelnen Pixel sagen, welche Farbe es haben sollte.

Es begann mit Pixeln.

Die Grafik früher Spiele wurde direkt in den Speicher geschrieben. Programmierer mussten die Speicheradresse kennen, die jeder Position auf dem Bildschirm entsprach. Um einen Punkt an einer bestimmten Stelle zu zeichnen, schrieb man einen Wert an diese Adresse — der Wert stand für die Farbe. Um eine Linie zu zeichnen, schrieb man eine Schleife, die der CPU sagte, sie solle von dieser Adresse zu jener alle N Bytes schreiben. Um eine Figur zu zeichnen, konvertierte man jede Pixelzeile in Hexadezimalzahlen, schrieb sie Zeile für Zeile ein, fand den richtigen Offset auf dem Bildschirm und platzierte diese Zahlen dort.

Dieser Vorgang heißt heute “Software-Rendering”. Damals hatte er keinen Namen, weil es keine Alternative gab — er hieß einfach “wie das Bild rauskommt”.

Als Carmack am ersten Wolfenstein arbeitete, stand er vor einem spezifischen Problem: Die Grafikarchitektur des IBM PC unterstützte kein sanftes horizontales Scrollen. Der Bildschirm aktualisierte sich, indem er den gesamten Frame auf einmal neu aufbaute, was zu Tearing führte, wenn sich das Bild bewegte — es sah aus, als würde es zittern. Er verbrachte enorme Zeit damit, die Low-Level-Register der Grafikkarte zu studieren, und fand einen Weg, die Hardware auszutricksen, um eine Funktion zu ermöglichen, die sie nativ nicht unterstützte, indem er sie per Software simulierte — und das Bild konnte sanft scrollen.

Es gab keine Dokumentation dafür, keine Vorkenntnisse. Er setzte sich einfach hin, wälzte Hardware-Handbücher und fand es Stück für Stück durch Versuch und Irrtum heraus.

Das war der grundlegende Arbeitszustand von Programmierern jener Zeit. Du standest nicht einem System mit Bedienungsanleitung gegenüber — du standest der Hardware selbst gegenüber. Du musstest die Hardware besser kennen als die Hardware sich selbst kannte, und in ihren Ritzen Platz finden.

Sound war ein Problem einer völlig anderen Dimension.

Das Soundsystem früher PCs war ein Summer — der PC Speaker. Er konnte nur einen Ton erzeugen: einen Piepton. Um damit Musik abzuspielen, musstest du die Vibrationsfrequenz dieses Summers steuern, mit extrem hoher Geschwindigkeit zwischen verschiedenen Frequenzen wechseln und die Nachhalleffekte des menschlichen Gehörs nutzen, um es klingen zu lassen, als würde eine Melodie gespielt. Diese Technik heißt PWM, Pulsweitenmodulation. Das Prinzip ist akustische Physik, die Umsetzung ist eine auf die Mikrosekunde genaue Timing-Steuerung. Wenn du dich auch nur ein klein wenig irrtest, kam nur Rauschen dabei heraus.

Es gab damals Programmierer, die专门 untersuchten, wie man mehr Klangschichten aus dem PC-Speaker herausholen konnte. Ihre Methode war, in extrem kurzen Zeitfenstern zwischen verschiedenen Frequenzen zu wechseln und so einen Pseudo-Stereo-Effekt zu erzeugen. Diese Technik wurde in der Demo-Szene der damaligen Zeit wie schwarze Magie behandelt. Du nimmst einen Summer, der nur “piep” kann, und machst etwas, das sich wie Musik anhört. Andere Programmierer, die es hörten, hatten keine Ahnung, wie du es gemacht hattest — weil das Prinzip nicht intuitiv war. Du brauchtest ein extrem präzises Verständnis der Timing-Steuerung, um es zu schaffen.

Als Adlib und Sound Blaster aufkamen, brachten sie FM-Synthese-Sound mit. Das war ein weiteres System, das man von Grund auf lernen musste. Das Prinzip der FM-Synthese ist, die Wellenform einer Frequenz zu verwenden, um die Wellenform einer anderen zu modulieren und so komplexe Klangfarben zu erzeugen. Unterschiedliche Modulationsparameter erzeugten unterschiedliche Instrumentenklänge. Die Programmierer mussten die Parameter selbst einstellen, selbst testen, welche Kombinationen wie ein Klavier klangen, welche wie eine Violine, welche wie eine Explosion. Es gab keine Standardantwort — alles hing vom Gehör und der Erfahrung ab. Damals hatte jeder Sound-Programmierer jeder Firma seine eigenen angesammelten Parametertabellen. Das waren im wahrsten Sinne des Wortes Betriebsgeheimnisse.

Das Designgefühl auf der Ebene der Spiellogik war eine weitere Form.

Keine Engine bedeutete keine fertige Kollisionserkennung, keine fertige Physiksimulation, kein fertiges KI-Framework. Wenn du wolltest, dass eine Figur an einer Wand stehenbleibt, anstatt hindurchzugehen, musstest du die Kollisionserkennung selbst schreiben, selbst definieren, was eine “Kollision” ist, und selbst entscheiden, wie sich Geschwindigkeit und Position der Figur nach der Kollision verändern.

Die Kollisionserkennung früher Spiele war einfach und brutal: Behandle sowohl die Figur als auch die Hindernisse als Rechtecke, prüfe, ob sich die beiden Rechtecke überlappen, und wenn ja, stoppe. Dieses Schema war schnell, aber das Problem war, dass echte Spielfiguren keine Rechtecke sind — sie sind Sprites mit Formen. Es gab immer eine Diskrepanz zwischen der rechteckigen Kollisionsbox und der tatsächlichen Grafik. Deshalb kam es in frühen Spielen oft vor, dass Kugeln, die eindeutig daneben gingen, trotzdem als Treffer gewertet wurden, oder dass Figuren stehenblieben, bevor sie die Wand sichtbar berührt hatten.

Die Spieler damals nannten das keinen Bug. Sie nannten es eine Spielfunktion. Weil sie nicht wussten, dass es auch anders gehen konnte.

Eine weitere Sache, die erwähnenswert ist: Frühe PCs hatten extrem wenig Speicher. 640KB war über ein Jahrzehnt lang die Hardware-Obergrenze. Diese Zahl kann heute nicht einmal einen einzigen Screenshot fassen. Die Programmierer mussten den gesamten Inhalt des Spiels — Grafik, Soundeffekte, Karten, Logikcode — in diese 640KB stopfen und dabei noch Platz für Laufzeitdaten lassen.

Wie? Kompression, blockweises Laden und Wiederverwendung.

Grafikkompression war eine eigene Disziplin. Programmierer mussten eigene Kompressionsalgorithmen erfinden, um Grafikdaten bei der Speicherung so klein wie möglich zu machen und sie bei Bedarf sofort zu dekomprimieren. Wenn die Karte zu groß war, teilte man sie in Blöcke auf, lud den Block, in dem der Spieler stand, und entlud ihn, wenn er ihn verließ. Heute heißt diese Technik Streaming. Damals hieß sie “keine andere Möglichkeit”.

Jedes Byte im Speicher war erkämpft. Beim Schreiben von Code mussten Programmierer ständig denken: Kann ich für diese Variable einen kleineren Datentyp verwenden? Kann ich bei dieser Logik mit weniger temporären Variablen auskommen? Kann ich die Anzahl der Speicherzugriffe in dieser Schleife reduzieren? Diese Denkweise ist für heutige Programmierer weitgehend unnötig. Moderne Hardware hat genug Speicherreserven, und Compiler sind intelligent genug in der Optimierung — du musst nicht jedes Byte zählen.

Aber die Programmierer jener Zeit trugen die gesamte Funktionsweise des Computers im Kopf. Von CPU-Befehlszyklen über das Speicherlayout bis hin zu GPU-Registern — sie mussten jede Ebene verstehen, denn jede Ebene war ein Spalt, den sie möglicherweise ausnutzen mussten.

Carmack schrieb einmal Code zur Berechnung der inversen Quadratwurzel, mit einem Kommentar, der “what the fuck” lautete. Dieser Code nutzte einen äußerst unintuitiven Bitmanipulations-Trick, um die inverse Quadratwurzel schnell zu approximieren, weit schneller als die Standard-Mathematikbibliothek — alles, um Rechenzeit im 3D-Rendering zu sparen. Dieses Codefragment wurde später zu einer Legende in der Programmiererkultur, immer wieder zitiert, weil es eine Denkweise repräsentierte, die inzwischen verschwunden ist: die Methode, Leistung aus Mathematik und Kreativität innerhalb von Hardware-Beschränkungen herauszupressen.

Die Spieleprogrammierer jener Zeit waren eher eine Mischung aus Ingenieuren und Wissenschaftlern. Das Problem, das sie lösten, war nicht nur “wie implementiere ich diese Funktion” — es war “kann diese Funktion innerhalb der physikalischen Grenzen dieser Maschine überhaupt implementiert werden?”

Das sind zwei sehr unterschiedliche Fragen.

Die erste Frage hat eine Standardantwort: in der Dokumentation nachschlagen, die API aufrufen, der Anleitung folgen. Die zweite Frage hat keine Standardantwort. Du musst die Antwort selbst erfinden. Manchmal existiert die Antwort, manchmal nicht. Du musstest zuerst herausfinden, in welchem Fall du dich befandest, und dann entscheiden, was zu tun war.

Die heutige Spieleindustrie ist ein massives kooperatives System. Engine-Firmen, Middleware-Firmen, Toolchain-Firmen — auf jeder Ebene arbeitet jemand daran. Entwickler stehen an der Spitze dieses Systems und konzentrieren sich auf den Inhalt selbst. Das ist Fortschritt, es ist eine gute Sache — es ermöglicht mehr Menschen, Spiele zu machen, und lässt den Umfang und die Komplexität von Spielen weit über das hinauswachsen, was früher möglich war.

Aber jene Ära, in der Spiele von Pixeln aus gebaut wurden, hatte etwas, das es heute nicht mehr gibt.

Du öffnest das Spiel und weißt, woher jeder Punkt auf dem Bildschirm kam, wie jeder Sound gemacht wurde, wie jede Kollision bewertet wurde. Denn das alles hast du Zeile für Zeile selbst geschrieben. Es gab nichts, was jemand anderes für dich getan hat.

Dieses Gefühl hat einen Namen. Es heißt vollständige Kontrolle.

Also, die Größen, die damals Spiele entwickelten — das waren wahre Größen. Wirklich beeindruckend.