Post Mortem: Fart Fiasco – Teil 4: Im Content-Generation-Labyrinth
Zurück zu Teil 3: Post Mortem: Fart Fiasco – Roboterchaos & iTürsteher
Apple will mein Spiel nicht, Google rennt man ständig hinterher, die DSGVO kriminalisiert gefühlt das gesamte Werbenetzwerk und auch sonst schien die Smartphone-Entwicklung problematisch und mühsam. Sollte ich das Projekt, in das schon so viel Arbeit geflossen war einfach aufgeben? Allem Chaos zum Trotz liefen einige Dinge auch ganz gut, z.B. die Entwicklung der Spielinhalte.
Überraschend gut: Canvas UI + Physik
Der allererste Konzept-Test, damals noch in Unity 5, basierte auf Sprite-Objekten im 3D-Raum. Während das grundsätzlich funktionierte, stieß ich aber relativ schnell auf das Problem, dass bei diesem rudimentären Ansatz wirklich so gut wie alles von Null auf selbst programmiert werden muss. Insbesondere die Arbeit mit dem Verhältnis von Sprites zueinander sowie Relationen zu den physischen Bildschirmrändern wurde schnell sehr mühsam.
Eine der interessantesten technischen Erkenntnisse dieses Projekts war für mich die Nutzbarkeit des Canvas UI-Systems für Szenengrafik. In vielen Game-Engines beschränkt sich das 2D-GUI-System auf die Entwicklung von HUDs. Doch in Unity ist der Canvas so vielseitig, dass er letztlich als komplexe Hilfsmittelsammlung für HUDs und Spielgrafik brauchbar ist. Die Arbeit mit 2D-Koordinaten im Pixelsystem, Drehpunkten, Grafikanimationen und der 2D-Physiksimulation vereinfacht sich hierbei erheblich. Ich hätte nicht gedacht, dass das so gut funktioniert, doch Fart Fiasco ist komplett in Canvas-Komponenten realisiert. Das Schweben und Gleiten der Kuh übernimmt dabei die Physik-Engine, die Kraftimpulse und andere physikalische Größen bei Interaktionen und Objektreaktionen erhält.
Mehr Spieltiefe durch Labyrinth-Generator
Das Spiel besteht aus zwei Arten von Leveln. Einerseits gibt es ca. 50 handgestaltete Herausforderungen mit individuellen Leveldesigns, das teils auch ungewöhnliche Formen einnimmt.
Mit dem ursprünglichen Ziel für Mobile zu entwickeln, existiert zudem ein automatisch generiertes Level, das ein endloses Spiel oder das freie Sammeln von Punkten ermöglicht. In der letztlichen Desktop-Version wurden beide Teile kombiniert. Man spielt zunächst im freien Modus zufällig generierte Level und kann mit den so erspielten Punkten die individuellen Level in Form so genannter Quests freischalten.
Das Generieren unvorhersehbarer Levelverläufe war eine weitere neue Herausforderung für mich, da ich mich nie zuvor mit prozeduralen Inhalten beschäftigt hatte. Das ganze Spielbrett wird dabei in sechseckige Felder aufgeteilt. Zwar werden die Daten im Code über ein zweidimensionales Array realisiert, doch ergibt die Auswertung als Zellen mit jeweils sechs möglichen Ausgängen eine viel organischere und interessantere Wegform als ein rechteckiges Raster. Gleichzeitig lässt sich eine angrenzende Zelle durch errechnen des zweidimensionalen Array-Index schnell und einfach bewerkstelligen.
Im nächsten Schritt errechnet ein Labyrinth-Generator-Algorithmus Wege durch das hexagonale Raster. Dabei entstehen auch unterschiedlich lange Wege und Sackgassen. Dieses Video visualisiert die Schritte, die das Verfahren durchläuft.
Sobald die erlaubten Bewegungspfade für die Kuh feststehen, ergeben sich daraus indirekt automatisch die Positionen für mögliche Objekte wie Hindernisse oder Sammelobjekte.
Die Platzierung der einzelnen Szenenobjekte basiert auf einer zufälligen Verteilung entlang der gefundenen Wände. Für visuelle Varianz bei gleichzeitiger garantierter Lösbarkeit müssen einige Bedingungen berücksichtigt werden. Die zufällig festgelegte Größe eines Hindernisses muss stets ein Minimum erfüllen, damit das Objekt gut sichtbar ist. Gleichzeitig darf es nie zu groß werden, da der entstehende Korridor sonst zu eng für die Kuh wird und damit eine unbeabsichtigte Wegsperre entsteht.
Um die Level abwechslungsreich zu gestalten und eine Steigerung im Spielverlauf zu ermöglichen, kommen zudem weitere Bedingungen zum Einsatz, die in Form von Scriptable Objects gespeichert wurden. Dadurch konnte ich die grafische Programmoberfläche von Unity nutzen, um Bedingungen und Konfigurationen für den Levelgenerator zu hinterlegen. Zum Beispiel ist es möglich festzulegen, wann welche Arten von Hindernissen entstehen, welche Sammelressourcen auftauchen können und mit welcher Wahrscheinlichkeit Elemente vom Generator gestreut werden.
Noch mehr eigene Tools
Gerade in diesem Projekt erwies sich Möglichkeit eigene Editorerweiterungen für Unity zu schreiben, als extrem praktisch. Das Projekt besteht aus vielen einzelnen Bausteinen, die auf bestimmte Art miteinander zusammenspielen müssen. Zum Beispiel hatte ich allgemeine, levelübergreifende Spielobjekte in einer global Szene zusammengefasst. Die einzelnen Level-Szenen enthalten dagegen nur die individuellen Szenenobjekte, die aber ohne die globale Gameboard-Szene nicht darstellbar sind. Meine Editor-Erweiterung erkennt z.B. wenn ein solches Level im Editor geöffnet wird und bietet direkt an, die nötigen Abhängigkeiten ebenfalls zu laden.
Auch der Zugriff auf bestimmte Elemente wie die Kuh, die sich irgendwo in der Objektehierarchie befindet, aber eindeutig identifizierbar ist, konnte ich beschleunigen. Mit nur einem Klick auf die Select Cow-Schaltfläche wählt Unity nun genau dieses zentrale Objekt aus. Ähnliches gilt für Konfigurationen, die sich über verschiedene Komponenten und GameObjects hinweg erstrecken und mit dem eigenen Editor-UI an einer Stelle zugänglich werden. Eine meiner persönlichen Lieblingsfunktionen in diesem Zusammenhang ist jedoch der Problem-Finder, den ich schon für A Room Beyond geschrieben und nun erweitert bzw. angepasst habe. Er untersucht die Szene auf Unplausiblitäten hin, wie z.B. eine Abweichung zwischen den maximal erreichbaren Punkten pro Level und den tatsächlich verteilten Sammelobjekten, die diese Summe bilden müssen. Wird ein Fehler gefunden, erscheint er in einer Liste. Ist der Fehler programmatisch behebbar, wird eine entsprechende Reparaturfunktion per Klick angeboten, andernfalls wird zum Objekt gesprungen, das den Fehler enthält. Eine super praktische Hilfestellung, um sich vor Flüchtigkeitsfehlern zu schützen, weil man mit zunehmender Komplexität schnell den Überblick über das eigene Softwarekonstrukt verlieren kann.
Fokus auf den Desktop
Letztlich entschied ich mich dazu, die Smartphone-Unterstützung komplett abzubrechen, zumindest aber eine Desktop-Version anzubieten. Mit der Veröffentlichung auf Steam hatte ich zuletzt gute Erfahrungen gemacht und das Spiel war ja auch schon fast fertig. Mit ein wenig Mehraufwand wäre Fart Fiasco auch mit Maus+Tastatur bzw. Controller grundsätzlich spielbar. Was sollte da noch schiefgehen?
Weiter in Teil 5: Post Mortem: Fart Fiasco’s fatales Finale