Benutzer-Werkzeuge

Webseiten-Werkzeuge


theorie:patterns

Hilfreiche Entwurfsmuster

Wenn du schon programmieren oder skripten kannst, dann stehst du bei mittelgroßen Projekten vermutlich wie ich vor der Frage: Wie strukturiere ich den Code, wenn ich auf der Suche nach Bugs nicht immer wieder über 1000 Zeilen Code lesen will??

Ein spielbares Minigame in Unity hat bei mir 250 Zeilen Code, die sich auf 4 Dateien verteilen. Das Player Skript ist mit 115 Zeilen die größte Datei. Darin finde ich mich noch gut zurecht, da brauche ich mir nicht die Mühe machen den Code elegant zu strukturieren. Sobald ein Spiel aber ernst zu nehmend groß wird, gehen die Zeilen in die 10 Tausende. Google sagt sogar: „Für ein modernes kommerzielles Computerspiel schreiben oft bis zu 30 Programmierer schätzungsweise über eine Million Zeilen Quellcode.“

Auch Romane werden in Kapitel eingeteilt, also müssen wir irgendwie sinnvolle Aufteilungen für unseren Code finden.

Was bietet uns Unity?

Sehen wir uns an, was wir in der Unity Engine bereits benutzen. Sie „zwingt“ uns, einzelne Szenen Elemente als GameObject zu behandeln und wenn wir Code ausführen wollen, dann sind die MonoBehaviour Methoden Start, Update, FixedUpdate usw unsere Einstiegspunkte. Das heißt es wurde eine Klasse (= Datenklumpen Vorlage) angelegt, von der ich vorgefertigte Code Bausteine verwenden kann ohne sie selbst / nochmal schreiben zu müssen.

Das nennt sich Vererbung. Gute Sache, die für Minigames völlig ausreicht. Das Problem daran ist, dass wir nicht kreuz und quer erben können, sondern einen sauberen Stammbaum brauchen. Wenn Klassen voneinander erben, dann ist Inzest verboten. Standardmäßig erbt alles von MonoBehaviour, dadurch sind unsere Möglichkeiten eher begrenzt.

Hier kommen Interfaces ins Spiel. Sie definieren keine Codebausteine, sondern nur Aktionsnamen. Wenn eine Klasse von so einem Interface erbt, dann sage ich damit nur, dass die Klasse eine Aktion bereitstellt. Was sie macht, muss ich im Code der Klasse selber schreiben. Wie hilft uns das also?

Wir können zum Beispiel ein Interface namens IClickable schreiben, die die Methode "Interact()" definiert. Im Player Skript (oder wo sonst User Input abgehandelt wird) sind wir dadurch unabhängig von allen einzelnen Szenenelementen, auf die man klicken kann. Wir brauchen nur das GameObject mit TryGetComponent fragen, ob es das Interface IClickable bereitstellt, und falls ja seine Interact Methode ausführen. Was im Einzelnen passiert steht brav in der Klasse des Objekts, auf das geklickt wurde.

Objektorientierung

Ganz allgemein bietet die Verwendung der objektorientierten Programmiersprache C# die Möglichkeit, meinen Code in Klassen und Methoden zu strukturieren. Das heißt ich denke mir Namen für Code Bausteine aus, die ich überall verwenden kann wo ich sie brauche ohne den Code kopieren zu müssen. Wenn ich also in so einem Code Baustein einen Bug fixe, dann brauche ich nicht mehr alle Stellen suchen, wohin ich diesen Bug kopiert habe.

Außerdem kann ich dadurch den Code lesbarer machen. In FixedUpdate zum Beispiel passiert sehr viel nacheinander. Ich kann für jedes „Kapitel“ eine Methode erstellen, sodass in FixedUpdate nur noch die Liste der Kapitelnamen steht und ich schnell einen Überblick bekomme, was in welcher Reihenfolge passiert. In den Methoden sehe ich dann nach, wenn in diesem Teilbereich etwas nicht funktioniert.

State Machine

Was Animationen angeht, bietet Unity die Möglichkeit Zustände anzulegen, die teilweise ineinander übergehen können und sogar grafisch angezeigt werden. Wir können die Struktur aber auch selber bauen und dazu benutzen, dass wir unser Player Skript sinnvoll auf verschiedene Dateien aufteilen können. Jede Datei kümmert sich nur um das, was im jeweiligen Zustand möglich und wichtig ist.

Observer Pattern

Ein Observer kann sich sozusagen anmelden, dass ihn ein bestimmtes Ereignis interessiert, in dessen Fall er Code ausführen möchte. Zum Beispiel muss die Lebenspunkte Anzeige irgendwie mitbekommen, wenn der Spieler Schaden nimmt. Die Darstellung will ich nicht im Player Skript steuern, sondern hauptsächlich in der Lebenspunkte Anzeige. Das Player Skript muss nur die Möglichkeit bereitstellen, dass er im Fall des Lebenspunkte Verlustes die angemeldeten Beobachter durchgeht und benachrichtigt.

Singleton

Ein Singleton stellt die Möglichkeit bereit, eine für alle GameObjects interessante Klasse für alle sichtbar bereitzustellen. Die Einstellungen des Spiels zum Beispiel. Dabei wollen wir verhindern, dass jeder sein eigenes Objekt dieser Klasse erstellt und seine eigenen Werte setzt, von denen niemand sonst etwas mitbekommt. Wir wollen, dass alle Zugriff auf das selbe gemeinsame Objekt haben und das auch benutzen. Also setzen wir den Klassen Konstruktor auf privat, sodass nur die Klasse selbst eine Ausprägung erstellen kann. Außerdem geben wir einen public static Zugriffspunkt auf eine gemeinsame Instanz, die wir private static speichern und nur dann erstellen, wenn sie noch nicht initialisiert wurde. Beim ersten Zugriff wird also das gemeinsame Objekt erstellt und danach arbeitet jeder direkt mit dem gemeinsamen Objekt.

theorie/patterns.txt · Zuletzt geändert: 2023/03/11 10:40 von spielzimmer

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki