Mittwoch, 25. Mai 2011

Actionscript 3: PlayerClass

Wie ich schonmal gesagt hatte schreibe ich gerade ein Spiel mit Actionscript. Desshalb kommt jetzt auch etwas spezifischeres, diesmal werde ich euch zeigen, wie man eine effektive Datenstruktur für den Spieler anlegt. Es geht ersteinmal los mit der Theorie:

Die Frage ist "Wie gestalte ich meine Spielerklasse?". Man könnte ja einfach alles zusammen klatschen, also sämtliche möglichen Zustände, Bewegungsabfragen, Aktionen, Kollisionen, Grafiken, Animationen, etc. Dies ist jedoch keine gute Idee, eure Klasse wird sich mit soviel unnötigen Informationen aufblähen, so dass wir im Nachhinein nur noch sehr schwer die Übersicht behalten können. Desshalb werden wir Grafik von Logik trennen und heute ist auch nur der Logikpart dran, da ich die Grundlagen für die Grafik in meinem Eintrag über das Erstellen eines eigenen MovieClips abgehandelt habe.

Wir wollen in dieser Klasse mal die Grundlagen legen, woraus dann jeder seine eigene Klasse bauen kann und diese dann noch beliebig erweitern kann. Unser Rezept sieht wie folgt aus:
-Bewegungsgeschwindigkeit
-Bewegungsrichtung
-Hitpoints
-Inventar
-Fall

1.) Die Klasse

public class Player
{
}

Diesmal ist die Klasse von gar nichts abgeleitet, da das Objekt selbst niemals auf dem Spielfeld sichtbar ist. Für die Pen & Paper Fans unter euch, stellt es euch einfach vor wie ein Charakterbogen oder ein Datenblatt. Wir notieren hier nur alles notwendige, damit wir auf dem Spielfeld den Charakter was machen lassen können.

2.) Die Attribute

private var _tp:int;
private var _inventory:Array;
private var _dx:int;
private var _dy:int;
private var _fall:Boolean;

Das sind ja schon ein paar Attribute die wir benötigen und es werden je nach Spiel nur noch mehr oder stellenweise andere (wie z.B. das austauschen von _fall). Auffällig ist, dass ich sowohl jedes Attribut als private deklariert habe aber auch, dass jede Attributsbezeichnung mit einem _ beginnt. Das hat den Grund, damit wir später ganz genau wissen, welche Variable ist ein Attribut und welche existiert nur wärend eines Methodenaufrufs. Von oben nach unten gelesen sagen die Werte aus: Trefferpunkte, Inventar, Bewegungsrichtung und Geschwindigkeit auf der X-Achse, Bewegungsrichtung und Geschwindigkeit auf der Y-Achse, fällt der Spieler (interessant für Plattformer o.ä.).

3.) Der Konstruktor

public Player()
{
_tp = 100;
_inventory = new Array();
_dx = 0;
_dy = 0;
_fall = false;

createInventory();
}

Der Konstruktor arbeitet wieder ohne Parameter und initialisiert die Standardwerte für jedes Attribut, dieser muss bei mehr Attributen natürlich erweitert werden. inventory wird als leeres Array angelegt und mit der Methode createInventory() so weit gefüllt, wie ihr es vorgesehen habt.

4.) createInventory()

private function createInventory():void
{
var item:InventoryItem;

//hier kommt jetzt euer Inventory Code
//...
//...
//...

_inventory.push(item);
}

Hier muss ich anmerken, dass der Datentyp InventoryItem ein imaginärer Typ ist, diesen müsstet ihr erst als extra Klasse definieren, bevor ihr ihn verwenden könnt. Es ist also ein Dummy Bezeichner. Wie ihr nun euer Inventar macht ist euch überlassen, zum Schluss wird mit dem push Befehl euer Item in das Inventar gebracht. Hierbeiliefert der Befehl die neue Länge des Arrays zurück, dies ist aber derzeit für uns unwichtig.

5.) Getter

public function get TP():int
{
return _tp;
}

Einige werden sich jetzt fragen "Was ist ein Getter?". Mit hilfe einer Getter Methode können die Werte von Attributen ausgelesen werden, dies ist nützlich wenn viele Attribute als private oder protected gekennzeichnet sind aber ihr Wert für das Programm wichtig ist. Jetzt werden einige garantiert sagen "Das kann ich aber auch mithilfe von Public Attributen erreichen." und ja, ihr habt recht. Ich verwende lieber Getter, weil ich damit ein versehentliches Überschreiben des Wertes verhindern kann, da ein Getter nur Leserechte habt. Es ist also an der Zeit für jedes Attribut, dass für die "Außenwelt" wichtige Werte hat, einen Getter anzulegen, dass ist primär nervige Copy Paste Arbeit, danach wird es aber einfacher Fehler, die durch versehentliches Schreiben passieren könnten, zu vermeiden.

6.) Setter

public function set TP(tp:int):void
{
_tp = tp;
}

Der Setter ist das genaue Gegenteil zum Getter, er setzt Werte und kann keine Lesen. Hier müsst ihr euch wieder selbst bewusst sein, welches Attribut einen Setter braucht.

7.) Timer [optional]
Dieser Punkt ist Optional und desshalb werde ich nur ein Erklärung machen, anstelle eines Code Beispiels. Es kann sein, dass ihr getimte oder im Intervall wiederkehrende Aktionen des Spielers habt, dann brauch der Spieler natürlich auch dafür seine Timer, diese lassen sich einfach als Attribute deklarieren. Dazu eine private Methode als EventHandler und zwei public Methoden zum Auslösen und Anhalten des Timers. Der Timer wird wie alle anderen Attribute im Konstruktor initialisiert. Wenn ihr doch hierfür ein Codebeispiel braucht, dann lasst es mich wissen, dann werde ich euch ein vernünftiges nach liefern.


So, dass war mal was anderes. Eine Klasse, die komplett nur dazu da ist, Daten zu halten und keine direkte Veränderung des optischen Outputs zu erzeugen. Es ist halt "nur" euer Notizzettel aber ihr werdet merken, wenn ihr sowas benutzt, wie leicht sich dann Fehlerquellen einschränken und Vermeiden lassen. Stellt euch nur mal vor ihr hättet mehr Daten als ich im Verlauf dieses Beispiels angegeben hätte und das alles zwischen irgendwelchen Daten und Rechnungen für die Graphik. Das ist doch schon dann ganz schön unübersichtlich.

Wie imemr gilt: Das benutzen des gegebenen Quellcodes ist auf eigene Verantwortung.

Keine Kommentare:

Kommentar veröffentlichen