1. Einführung
Das erste Spiel eines Gamers nimmt eine besondere Rolle in seinem Herzen ein. Bei mir war es damals Super Mario World für das SNES-System. Heutzutage bietet es die Videospielfirma Nintendo innerhalb der Virtual Console, die das Spielen von Retro-Klassikern auf Vertretern der neueren Konsolengeneration (Wii U, Switch, Nintendo 3DS) ermöglicht, zum Download an. Dass dieses Spiel eines Tages eine Einführung in die Programmiersprache Lua ermöglichen würde, haben die Entwickler um 1990 vermutlich nicht kommen sehen. In diesem Artikel geht es genau darum: Grundzüge der Programmiersprache Lua anhand von Super Mario World zu erlernen. Doch wie soll dieses hehre Ziel umgesetzt werden? \(\Longrightarrow\) Durch ROM-Hacking.
Nun, genauer gesagt ist es kein ROM-Hacking im klassischen Sinne, das man heutzutage in zahlreichen YouTube-Videos präsentiert bekommt. Dort wird häufig nur ein WYSIWYG-Editor verwendet, in den die ROM geladen und per Drag-and-Drop die Spielelemente auf dem Bildschirm verschoben werden. Das kann mittlerweile auch der Mario Maker (und zwar in weitaus besserer Qualität). Wir gehen anders vor: Wir laden die ROM des Spiels in einen Emulator und nehmen über eine Lua-Schnittstelle Manipulationen an der emulierten Hardware bzw. den Speicheradressen (und den dort befindlichen Werten) vor. Du fragst Dich vielleicht, was ein Emulator ist. Wikipedia definiert treffend:
Als Emulator (von lateinisch aemulari, „nachahmen“) wird in der Computertechnik ein System bezeichnet, das ein anderes in bestimmten Teilaspekten nachbildet.
Der Emulator, den wir verwenden (BizHawk) emuliert das SNES-System derart, dass ROMs (Read-Only-Memory, Festwertspeicher) des jeweiligen Spiels darauf verwendet werden können. In der Speedrun-Szene werden insbesondere bei sogenannten Tool-Assisted Speedruns (TAS) Emulatoren eingesetzt, um entweder bestimmte Glitches (Spielfehler) frame-perfect (auf den Frame genau) auszuführen oder einfach auf Fehlersuche zu gehen.
2. Download des BizHawk-Emulators und Aufbau des Setups
Zunächst einmal benötigst Du den BizHawk-Emulator. Diesen kannst Du Dir unter der folgenden Adresse herunterladen: https://github.com/TASVideos/BizHawk/releases/
Wähle die aktuellste Version (zum Zeitpunkt dieses Tutorials ist dies die Version 2.2.1)
Nach der erfolgreichen Installation solltest Du mit dem Doppelklick auf EmuHawk.exe folgendes Bild zu Gesicht bekommen:
Zudem sollte auf Deinem System Lua installiert sein: https://www.lua.org/download.html
Zuletzt ist eine ROM des Spiels "Super Mario World" vonnöten.
---------------
Disclaimer
Bevor Du weiterliest, möchte ich darauf hinweisen, dass es von Nintendo nicht gerne gesehen wird, alte Nintendo-Klassiker auf Smartphones, Tablet oder PCs zu emulieren. Auch ich vertrete die Auffassung, dass Du mindestens eine Kopie des Spiels käuflich erworben haben solltest (entweder über den Virtual Console Store oder als SNES-Modul), bevor Du das Spiel emulierst. Solltest Du eine Kopie besitzen, wünsche ich Dir viel Spaß beim Rest des Tutorials.
---------------
Wichtig: Lade Dir die US-Version herunter. Ziehe das .sfc-File per Drag-and-Drop in das Emulator-Fenster. Wenn alles richtig funktioniert, solltest Du Folgendes sehen können:
Während das Spiel innerhalb des BizHawk-Emulators läuft, kannst Du in Lua geschriebene Skripts mit der Dateiendung ".lua"
ebenfalls in den Emulator ziehen, der dann im Optimalfall das programmierte Verhalten auslöst. Doch wie was kann man eigentlich alles programmieren?
3. MarI/O - Ein neuronales Netz lernt Level 1 auf Yoshi's Island
Wer sich mit automatischer Gesichtserkennung oder dem Aufspüren von Tumorerkrankungen in Zellgewebe beschäftigt, wird früher oder später mit Machine Learning in Berührung kommen. Hierbei lernt die Maschine (der Computer) selbst, welche Eigenschaften in den auftretenden Daten (Gesichter oder Gewebsaufnahmen) zu welchem Ergebnis (die Zuordnung von Gesichtern zu Menschen oder die Detektion von mutiertem Gewebe) passen. Dieses Wissen generiert der Computer aus Daten, die vom Menschen bereitgestellt und in Trainingssessions "gelernt" werden. Anschließend ist der Computer zur selbsständigen Zuordnung in der Lage. Auch dieser sehr spannende Zweig der Informatik fußt auf mathematischen Modellen. Ein oft in der Bildverarbeitung eingesetzter Ansatz ist das "neuronale Netz", das wiederum auf Matrizenrechnung (Lineare Algebra) beruht.
Doch nicht nur in der Bildverarbeitung, sondern auch im Bereich der Programmierung von "intelligenten" NPCs kommen neuronale Netze zum Einsatz. Ein tolles Beispiel, das man theoretisch gegen Ende des Studiums selbst entwickeln kann, wenn man die entsprechenden Vorlesungen und Vertiefungsfächer gewählt hat, ist eine künstliche Intelligenz zum Lösen von 2D-Sidescroller-Spielen ganz nach dem Vorbild von MarI/O:
All das ist über die Lua-Schnittstelle des BizHawk-Emulators implementierbar. Wir werden innerhalb dieser Tutorial-Reihe zwar kein neuronales Netzwerk programmieren, das ein Level zu lernen imstande ist (dafür ist einfach zu viel Expertenwissen im Bereich "neuronale Netze" nötig). Vielmehr werden wir kleine Skripte schreiben, die z. B.
- Mario ungebremst in eine bestimmte Richtung laufen lassen
- Den Powerup-Zustand modifizieren
- Unverwundbar machen
- Den Ingame-Level-Timer verlangsamen/schneller machen
- 99 Extraleben spendieren
4. Hello World
Um eine Manipulation der dafür nötigen Speicheradressen durchzuführen, benötigen wir eine sogenannte RAM-Map (also eine Übersicht, in der die Speicheradressen samt Bedeutung aufgelistet und die über unser Lua-Skript angesprochen werden können).
Eine solche RAM-Map findest Du hier: https://www.smwcentral.net/?p=map&type=ram
Die einzelnen BizHawk-spezifischen Funktionen, die innerhalb von Lua aufgerufen werden können, sind in einer API übersichtlich dokumentiert: http://tasvideos.org/Bizhawk/LuaFunctions.html
Wir werden als erstes Mini-Skript ein Hello-World-Programm schreiben, das ein 200x200 Pixel großes Fenster im Emulator erzeugt, das wiederum den Text "Hello World!"
enthält (also noch keine Manipulation der Speicheradressen; diesen Spaß heben wir uns für später auf). Dafür erzeugen wir zunächst eine Canvas über die Funktion createcanvas
, die als Argumente die Länge und Breite des Canvas (in Pixeln) erhält:
local canvas = gui.createcanvas(200, 200);
Je nach gewünschter Funktionalität, sucht man die Funktionen in dem API-Reiter.
Hier wählt man z. B. gui aus und erhält alle Möglichkeiten angezeigt, mit der eine GUI erzeugt und gestaltet werden kann. Das Keyword local
zeigt an, das wir eine lokale Variable erzeugen. Dieser weisen wir den Rückgabewert von createcanvas (unser Canvas) zu. Durch
canvas.Clear(0xFF000000);
schwärzen wir den Bildschirm. Durch Veränderung des Arguments 0xFF000000
kannst Du andere Hintergrundfarben auswählen.
canvas.DrawText(0, 0, "Hello World!");
erzeugen wir an der Pixelposition \((0,0)\) den Text "Hello World!"
Das fertige Lua-Skript besteht also aus nur \(3\) Codezeilen:
local canvas = gui.createcanvas(200, 200);
canvas.Clear(0xFF000000);
canvas.DrawText(0, 0, "Hello World!");
Ziehst Du das Skript nun in den laufenden Emulator, erhältst Du folgende Ansicht:
Die Lua-Console interessiert uns zunächst einmal noch nicht. Diese wird erst in den weiteren Tutorials relevant.
Autor: Florian André Dalwigk
Das Mitglied hat durch den Artikel 50 Bonuspunkte erhalten. Schreib auch du einen Artikel.