Tutorial C# – Charakterklassen System – Basics

03 Okt 2013 Posted by: Comments: 0 In: Allgemein, C#, Programming, Tutorial, Unity3D

Hi Leute,

ich möche euch gerne zeigen wie man relativ einfach ein kleines Charakterklassen System zaubert. Mit verschiedenen Klassen, Verhalten etc. Ihr kennt diese Systeme sicherlich, denn sie kommen in fast jedem RPG vor. Sicher hat auch jeder schon mal daran gedacht, sowas umzusetzen (oder auch nicht;) ).
Wo wir schon beim Umsetzen sind: natürlich bleibt das Ganze rudimentär und nimmt keine Ausmaße großer MMO´s etc an – aber wenn das System einmal verstanden ist, könnt ihr daraus Einiges machen, wenn ihr wollt.

Zuerst einmal der Ausgangspunkt: angenommen das Spiel hat 3 spielbare Klassen (Warrior, Mage, Priest) und verschiedene NPC´s (Boss, Monster, Merchant). Wie kann man nun vorgehen ohne bestimmte Sachen 6 mal zu scripten? An dieser Stelle kommen wir schon zum ersten Problem, dessen Lösung aber garnicht so schwer ist!

Jetzt kann man sich erstmal die Frage stellen: Was haben diese 6 gemeinsam? Zuersteinmal: es sind „Entitäten“. Aber ein Baum oder ein Haus ist defacto aber auch eine „Entität“ – sie „sind“ (…natürlich nur virtuell).
Ok, sagen wir einfach es sind „Units“. Jede Characterklasse, jeder NPC ist zuerst eimal eine „Unit“. Wie geht es nun weiter in der Hierachie? Ganz einfach: Was haben alle Monster, Bosse oder Merchants gemeinsam? Sie sind NPC´s… und NPC`s wiederum Units.
Und was haben alle Warriors, Mages und Priests gemeinsam? Sie sind spielbare Character… nennen wir sie einfach „Heroes“ und natürlich sind alle spielbaren Klassen vom Typ Hero eine Unit.

So ergibt sich also eine Art Baum …oder eher die Wurzel eines Baumes;)

unbenannt-2v8psl

Bevor es ans Scripten geht, gibt es Dinge, die alle Units gemeinsam haben:
Sie können sich bewegen oder haben zumindest die Möglichkeit es zu tun.
Sie können Attacken ausführen oder andere Fähigkeiten nutzen. Aber bevor jetzt schon über Fähigkeiten nachgedacht wird, bleiben wir erstmal bei der Bewegung und fangen an die Scripte anzulegen.

Diese Basisklasse von der alle Units erben, nenne ich einfach mal Unit.cs und die sieht so aus:

Wie man sieht, ist NPC nicht vom Typ MonoBehaviour, sondern vom Typ Unit. Da aber Unit vom Typ MonoBehaviour ist, vererbt sie diese Eigenschaften und damit kann die NPC Klasse auf alles zugreifen was MonoBehaviour so mit sich bringt und nicht nur das – die NPC-Klasse kann auf alle Variablen zugreifen, die in Unit.cs public sind. Hier könnte man auch internal oder protected nutzen.
Wichtig ist aber, dass alle Variablen und Methoden nicht private sind.
Um das mal zu verdeutlichen nehmen wir als Beispiel noch die guten alten Healthpoints ins Spiel, gehen nochmal in die Unit.cs und machen folgendes:

Getters und Setters zum nachlesen: http://www.dotnetperls.com/property

Nun hat JEDE erbende Klasse den Zugriff auf Healthpoints (aber nicht auf _healthpoints! brauchen wir auch nicht) und kann diese setzen oder anderweitig verwenden, zB. für Healthpoints im GUI.

Damit wir mit den Healthpoints auch was machen können, kann man jetzt noch eine Methode einbauen:

WICHTIG:
Damit auch diese Methode für alle erbenden (bzw. abgeleitete) Klassen verfügbar ist, ist auch sie public.
Nebenbei sparen wir uns das Ganze x mal in jede Klasse zu schreiben… 😉
Bei 20 Methoden und 100 Variablen ist das einiges an Zeitersparnis, vorallem, wenn man änderungen vornehmen möchte;)

Weiter geht´s mit dem Hero Script:

Wie bereits anfangs erwähnt ist auch Hero von Unit abgeleitet.

Nun wird das Ganze noch einmal gesplittet und sieht folgendermaßen aus:

Alles eigenständige Scripts versteht sich.

Die Heroes sehen folgendermaßen aus:

Warum wird das gemacht? So kann man sich die Möglichkeit vorbehalten, dass Heros noch andere Dinge machen können als NPC´s. Diese können dann von Hero zu Hero auch wieder variieren. Das ist natürlich kein Muss, denn eigentlich könnten alle spielbaren Klassen und NPC´s auch direkt von Unit erben. Wir machen es trotzdem;)

Nachdem nun die Klassen klar sind, kommt der nächste interessantere Schritt:

Die Klasse Unit (und damit alle anderen Klassen) wird näher definiert.
Da sich alle Units bewegen können, setzen wir genau dort an und geben ihr die Methode Movement() ohne Uberladung. Wenn man genau schaut und das sollte man in Tutorials ja eh 😉 liest man dort virtual… und genau: die Methode ist leer und das bleibt sie hier auch.

Was genau virtuals sind, könnt hier nachlesen: http://www.dotnetperls.com/virtual
Dort steht alles recht gut erklärt und ich will es nicht zum x-ten Mal wiederholen 😉

Die virtual bringt nun keine Performanceschübe etc. Zum Einen sagt sie, dass alle abgeleiteten Klassen diese Methode überschreiben dürfen, zum Anderen dient sie einfach dazu das „Skelett“ der Klasse genauer zu definieren und Gegensatz zur AdjustHealth-Methode kann kann die Movement-Mehtode für jede Klasse andere Anweisungen abarbeiten! Natürlch kann man die Methode auch dort bereits mit Anweisungen füllen und so eine Standardmethode für alle abgeleiteten Klassen erstellen.

Die 2 nächsten Beispiele zeigen den Gebrauch:

override erklärt: http://www.dotnetperls.com/override

Nun wird dank des overrides die bekannte Methode „überschrieben“ und ausgeführt. Wie gesagt, es bringt keinen echten Vorteil, nur weiß man nach Wochen noch, dass diese Methode bei allen anderen Klassen des gleichen Typs existieren muss, außerdem kommt so mehr Struktur in den Code.

In das Boss-Script schreiben wir nun:

Nun müssten sich Monster geradeaus bewegen (entlang der z-Achse) und Bosse immer im Kreis (entlang der z-Achse um ihre y-Achse)

Alle anderen Klassen machen fürs Erste nichts.

Apropos „machen“ – es wird Zeit en paar Primitive zu nutzen und die Scripte anzuhängen.
Aber nicht irgendwie!!! Nicht einfach Anhängen und los!
Das machen wir eleganter.

Wenn ihr euch ein paar Cubes, Capsules, Cylinder in die Szene gezogen habt, erstellt gleich noch 6 Materialien und benennt sie wie folgt:

None, Boss, Monster, Merchant,Player, Base – gebt ihnen ein paar schickere Farben…rot für Monster und Boss, sowie Grün für das Player Material sind recht sinnfällig. Dem Merchant habe ich ein blau verpasst. None ist pink und Base bleibt weiß.
WICHTIG: legt diese in einen Ordner namens „Resources“ !!! (ohne Anführungsstriche)
Wo dieser Ordner liegt ist egal, hauptsache die Materialien sind dort drinnen.

Danach erstellt ihr noch ein paar Tags – da wären Monster, Boss, Merchant sinnig. (wer hätte es geahnt) Den Player-Tag gibt es ja schon.

Wenn das soweit fertig ist kommt eine State Machine zum Einsatz – eine ganz einfache.
Dafür wird ein neues Script namens CreatureSetup angelegt.
Hängt dieses Script an eure GameObjects – und zwar NUR dieses.

In diesem Script ist auch ein enum, mit welchem wir im Inspector festlegen können zu welcher Klasse unser GameObject gehört (Ich bin so frei und gehe davon aus, dass ihr bereits aus zuvor angelegten Primitiven Prefabs gemacht habt, was nun die Arbeit erleichtern solte)

Schaut euch die GameObjects mal im Inspector an… da lässt sich jetzt myCreatureType einstellen!
Außerdem lassen sich die Healthpoints zu Beginn festlegen.

Wenn nun das Spiel gestartet wird, checkt die State Machine von welchem CreatureType das GameObject ist und hängt das entsprechende Script und Material an, außerdem setzt es gleich den richtigen Tag. Wohin das führt dürfte langsam klar werden… das Setzen von Healthpoints oder das Anhängen eines Talent-Scripts, entsprechend der Unit-Klasse, über diese State Machine steht nun nichts im Wege.

…aber noch sind wir nicht fertig!!!

Angenommen ihr müsst aus einem anderen Script von einem anderen GameObject auf das Klassen-Script zugreifen, hättet ihr vlt ein Problem, wenn diese nicht alle vom gleichen Typ wären!

Denn woher soll nun das Objekt von dem ihr auf eure Ziel zugreifen wollt, wissen welches Script dranne hängt?! Warrior? Merchant oder doch Boss?

Alles halb so schlimm, wir nehmen einfach das Unit-Script!

Nun erstellt ein neues GO in der Szene.
Erstellt ein Script Namens Test und hängt es an das neue GO.

Dieses Script Checkt das Level nach dem Tag „Player“ NACHDEM alle Units ihre State Machine abgearbeitet haben, denn die läuft in der Awake-Methode.
Außerdem „attackiert“ es den Player und zieht ihm 5 Healthpoints ab. Ihr könnt es in der Console nachlesen;)

Alternativ kann man hier auch noch ein switch case nutzen und direkt die am GO angehängte Klasse ansprechen. Wie man sieht ist AdjustHealth auch dort verfügbar!

Der letzte Schritt.
In die Klassen Priest, Warrior, Mage, Monster, Boss und Merchant wird jetzt noch folgendes geschrieben:

Hier nochmal für Faulpelze zum selber gucken (unity 3.5.5 required)

unitypackage

Nächster Teil –> Abilities <-- anlegen und nutzen

Leave a Comment!

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.