Jeder hat seinen persönlichen RPG Favoriten. Das RPG, an dem sich in Zukunft alle anderen RPGs messen müssen, um als gut empfunden zu werden. Für einige ist es ein Teil der Final Fantasy Reihe, für andere ein Teil der Ultima Reihe, andere wieder werden sagen, dass es Baldurs Gate ist. Für mich ist es Arcanum.
Ironischerweise ist es weder das erste RPG, das ich je gespielt habe, noch habe ich es bisher beendet. Trotzdem ist es das Spiel, dass für mich das ideal RPG darstellt. Die Frage ist nun, warum?
Für mich ist es das Spielgefühl, die Spielwelt, die Spielatmosphäre. Als ich es das erste mal gespielt habe, da habe ich noch nicht so viele RPGs gespielt, vor allem so gut wie keine der klassischen cRPGs und PnP sowie Tabletop kannte ich damals noch nicht. Desweiteren waren Steampunk und Cyberpunk auch noch unbekannte Settings für mich. Ich war also nicht darauf vorbereitet, was für eine Welt mich erwartet, was wohl auch gut so war, denn heutzutage gehe ich mit ziemlich hohen Erwartungen an so ein Szenario ran.
Künstler: http://browse.deviantart.com/?q=steampunk&order=9&offset=0#/d2o5unp
Die Welt von Arcanum zeichnet sich durch die 4 Gegensätze Technik-Magie, Gut-Böse und den Vermischungen beim aufeinandertreffen aus. Dies ist auch nicht einfach nur so zusammengeklebt, sondern eine richtige Gesellschaft entsteht. Wo in großen Städten Wesen leben, die sowohl Technik als auch Magie verwenden, zeigt sich auf dem Land je nach Ort eine gewisse Dominanz abzeichnet. Wo die Technik regiert sind die ansässigen Magieanhänger geschwächt. So soll man in einer Nebenquest für einen Magier eine Dampfmaschine zerstören, weil die Technik seine Macht behindert. Dieses gegenseitige Auslöschen der beiden Extreme spielt auch Part in der Charakterentwicklung. Es ist möglich einen Helden zu erschaffen, der sowohl Zaubern kann, als auch mit Technik umgehen kann, erwartet bloß nicht, dass er ein mächtiger Zauberer oder ein Technikspezialist ist, denn das wird euch das Spiel nicht ermöglichen, wegen der Weltdynamik. Charaktere die stark zu Technik neigen bekommen als Bonus eine Zauberresistenz, auch gegen die Heilzauber des Heilers. Dies ist mir, als ich damals das erste mal dieses Spiel spielte nie bewusst geworden. Zauberer auf der anderen Hand können zwar sehr starke Magie wirken und bekommen auf alle Magie verbundenen Sachen Boni, dürfen aber keine Technik verwenden, weder im Kampf noch in der Welt an sich. Selbst die Eisenbahn bleibt ihnen verschlossen.
Magier bekommen keine Fahrkarten
Die Gut-Böse Ballance spielt eine nicht zu unterschätzende Rolle in der Begleiterwahl und wie ihre Begleiter auf sie reagieren. Eure Begleiter werden euch verlassen, wenn eure Gesinnung mit der ihren im Konflikt steht, manchmal sogar angreifen, in dem Versuch euch zu töten. Ihr könnt auch keine Begleiter aus der Entgegengesetzen Gesinnung anwerben. Die Welt reagiert auf eure Taten, was ja heut zu tage eigentlich schon Standard ist/sein sollte. Erwartet also als übelst Böser Charakter in einer Hochburg voller Paladine (Metaphorisch Gesprochen) kein warmes Willkommen oder sogar einen Kuchen.
Die Story ist non linear und verändert sich, soweit ich das beurteilen kann, nicht nur anhand von Gut-Böse, Technik-Magie, sondern auch danach, wem gebt ihr was, welche Quests löst ihr, welche verbockt ihr und kauft ihr dem Zeitungsjunge die neueste Ausgabe des Lokalen Fischwickelpapiers ab oder nicht. Hier kann ich euch leider nicht zu viel berichten, da ich erst am anfang eines kompletten Playthrough bin. Wie schon gesagt, dass letzte mal habe ich es vor Jahren gespielt und damals nicht kompletiert. Soviel kann aber gesagt sein: Jemand will euch tot sehen, dafür hat man einen Zeppelin, auf dem ihr Passagier wart, abgeschossen und versucht nun alles um euch tot zu sehen. Desweiteren scheint ihr die Reinkarnation irgendeines Elfen Messias zu sein, der irgendeinen Bösen töten soll (ja, der Geistliche zu Anfang, Virgil, ist sehr hilfreich in Bezug auf die heiligen Schriften), danke aber ich glaube ich halte mich da raus, was bisher als gültiger Storyverlauf erscheint.
Ganz am Anfang, der Zeppelin ist unten, man bekommt einen Ring, soll einen Jungen finden und hat auch noch seinen Drink verschüttet, toll
Wie jedes RPG ist natürlich der Kampf ein zentrales Element, hier gibt es mehrere Möglichkeiten: Nahkampf, Fernkampf, Magie, Wurfwaffen. Dazu haben wir die Wahl zwischen Rundebasiert und Echtzeit, wir können jederzeit mit einem Knopfdruck wechseln. Ich persönlich bevorzuge den rundenbasierten Kampf, da der Echtzeit zu hektisch ist. Alles läuft zu schnell ab und man kann nicht ordentlich den Kampf steuern. Der rundenbasierte Modus hat den Nachteil, dass die Anzeige der verbrauchten AP etwas verwirrend ist. Die Anzahl der AP, die abgezogen werden, werden in Orange dargestellt. Nun kann man eine Aktion auch machen, wenn man nicht genug AP hat, indem man Mana/Energie aufbringt. Dies wird in Rot angezeigt, die beiden Farbtöne sind leider zu ähnlich, so dass man es nicht so leicht sieht. Wenn im Kampf die Energie/Mana von einem Teilnehmer auf <= 0 fällt, dann wird dieser Kampfteilnehmer Aktionsunfähig, bis er wieder Energie/Mana hat, das gilt auch für die Gegner. Man kann die Energie/Mana sehr gut mit Angriffen mit Stumpfen Waffen reduzieren und so den Gegner erst Ohnmächtig kloppen und dann den Handlungsunfähigen Gegner erledigen. Trotzdem bleibt der Kampf sehr fordernd, da Unterschiedliche Gegner unterschiedliche Taktiken fordern. Eure Begleiter helfen euch zum Glück, die werden aber vom Computer gesteuert und agieren selbstständig, leider manchmal nicht ganz so helle wie man es gerne hätte.
Die Charaktererstellung erlaubt einen viele Optionen. Zuerst wählt man Geschlecht, Rasse, Hintergrundgeschichte. Wir haben unsere typischen 2 Geschlechte, männlich und weiblich, die sich in Stats und Storyverlauf unterscheiden, es hat also tatsächlich merkbare Auswirkungen anstelle von bloßer Optik. Als Rassen stehen uns: Menschen, Elfen, Halb-Elfen, Halb-Orks, Zwerge, Halblinge, Gnome, Halb-Oger zur verfügung, die sich abgesehen von den Attributen und Resistenzen auch schon in der Präferenz von Magie und Technik unterscheiden. Die Hintergrundgeschichte des Charakters ist optional und erlaubt nochmal die Stats zu verändern. Nun nur noch einen Namen und wir kommen zum eigentlichen Spaßteil, den Attributpunkten. Wir können nach jedem Levelaufstieg Attributpunkte verteilen, dabei nicht nur auf Attribute, sondern auch auf Fertigkeiten zum Craften, besseren Treffen mit bestimmten Waffenkategorien oder an sich bestimmte Kategoriefertigkeiten wie z.B. gesellschaftliche Fertigkeiten und/oder Zauber verteilen. Dies verändert dann auch die Ausrichtung des Charakters auf der Skala von Technik und Magie. Alleine an Zaubern haben wir 16 Schulen a 4 Zaubern, also insgesamt 64 Zauber. Diese reichen von simplen Licht Zauber bis hin zur Manipulation der Zeit und dem Beschwören von mächtigen Kreaturen. Von den 8 Attributen sind jeweils 4 für physische (linke Spalte) und 4 für magische Talente (rechte Spalte) zuständig, die entsprechend korespondierenden Attribute sind gegenüber gestellt, so dass man nicht ständig das über 100 Seitige Begleitwerk (ich liebe so dicke "Handbücher") auf zu haben.
Arcanum ist ein wirklich ausgezeichnetes Spiel und immer noch ein Beispiel für eine ausgeklügelte Spielwelt mit fantastischen Charakteren und unglaublich viel Spaß. Wer lust auf das Spiel hat, man findet immer noch Kopien online oder man kauft es sich auf gog.com für 6$. Es läuft unter Win7 64bit Problemlos, wenn man mit dem Parameter -doublebuffer startet. Wenn ihr also lusst habt, dann taucht ein in eine bezaubernde Welt, die mal was anderes ist als immer nur der gleiche schwere Trübsin der modernen cRPGs, ich bin jedenfalls schon am Playthrough und danach wird Icewind Dale hinterher geschoben :D
Vielleicht sollte ich einen Blog parallel über meine Spielerlebnisse machen, was meint ihr?
Samstag, 28. Mai 2011
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.
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.
Sonntag, 22. Mai 2011
Actionscript 3: DebugConsole
Dies ist diesmal eine etwas einfachere aber trotzdem wichtige Komponente. Jeder, der in Actionscript komplexere Animation, also Animationen, die mehr sind als ein fliegendes Quadrat, erstellt wird nicht selten mit Fehlern zu kämpfen haben. Gegen syntaktische Fehler muss jeder selbst vorgehen aber dafür haben wir ja einen Compiler(oder ist es sogar ein JIT?). Logik Fehler sind schwerer auszubügeln. Desshalb wird wohl jeder schonmal die "trace" Funktion missbraucht haben. Das grundsätzliche Problem damit ist, die Output Konsole wird ziemlich schnell ziemlich voll und wichtige Werte gehen in der Menge einfach unter. Desshalb machen wir uns mal eine OnScreen Debug Konsole. Dazu brauchen wir auch nicht viel:
-eine Klasse die von der Klasse TextField erbt
-zwei Input Methoden
-eine Löschen Methode
Wie immer gilt, dies sind nur BareBones, alles ist individuell erweiterbar und steht zu euren freien Verfügung. Ich werde am Schluss von meinem AS3 vorhaben alle Tutorials nochmal sammeln und auf www.studium-informatik.net zusammengefasst veröffentlichen.
Nun aber ran an den Speck:
1.) Die Klasse
public class DebugConsole extends TextField
{
}
ganz einfach und übersichtlich und es wird nicht komplizierter. Wir erben von TextField, da das eigentlich alles trägt was wir brauchen, nun fügen wir nur noch hinzu :)
2.) Der Konstruktor
public function DebugConsole()
{
}
Dies ist Ausnahmsweise mal ein leerer Standardkonstruktor.
3a.) Schreiben, die Erste
public function write(_text:String):void
{
this.text += " " + _text;
}
Diese Methode fügt dem bisherigen Text der Konsole neuen Text hinten heran. Vor dem neuen Text wird sicherheitshalber ein Leerzeichen eingetragen, damit wir keine Wortschlangen wie "DiesistdocheineabsolutunleserlicheAusgabewiemansienichtrichtigauswertenkann." bekommen.
3b.) Schreiben, die Zweite
public function writeLine(_text:String):void
{
this.text += _text + "\n";
}
Was fehlte uns beim 1.? Genau Zeilenumbrüche, das machen wir jetzt. Es ist quasi das gleiche in Grün, nur jetzt mit einem Zeilenumbruch hinten an dem Text dran. Kein Originaltext wird zerstört, dies gilt für beide Funktionen.
4.) Nun Löschen wir es wieder
public function clear():void
{
this.text = "";
}
Die letzte Methode der Klasse leert den kompletten Output, dies ist vor allem nützlich, wenn man ständig verändernde Werte von Variablen (wie die X und Y Koordinaten eines sich bewegenden Objektes) zu beobachten, ohne das ellenlange Texte entstehen. Es wird aber nicht wirklich der Text gelöscht, sondern nur der bisherige Text mit einem leeren String überschrieben. Hat aber den gleichen Effekt und frisst auch nicht mehr Speicher.
So, nun haben wir eine DebugConsole. Viel Spaß damit. Falls euch dass Tutorial gut gefallen hat, dann empfehlt mich ruhig weiter. Desweiteren gilt wie bei jedem Code, den ihr im Internet findet: Benutzung ist auf eigene Verantwortung
-eine Klasse die von der Klasse TextField erbt
-zwei Input Methoden
-eine Löschen Methode
Wie immer gilt, dies sind nur BareBones, alles ist individuell erweiterbar und steht zu euren freien Verfügung. Ich werde am Schluss von meinem AS3 vorhaben alle Tutorials nochmal sammeln und auf www.studium-informatik.net zusammengefasst veröffentlichen.
Nun aber ran an den Speck:
1.) Die Klasse
public class DebugConsole extends TextField
{
}
ganz einfach und übersichtlich und es wird nicht komplizierter. Wir erben von TextField, da das eigentlich alles trägt was wir brauchen, nun fügen wir nur noch hinzu :)
2.) Der Konstruktor
public function DebugConsole()
{
}
Dies ist Ausnahmsweise mal ein leerer Standardkonstruktor.
3a.) Schreiben, die Erste
public function write(_text:String):void
{
this.text += " " + _text;
}
Diese Methode fügt dem bisherigen Text der Konsole neuen Text hinten heran. Vor dem neuen Text wird sicherheitshalber ein Leerzeichen eingetragen, damit wir keine Wortschlangen wie "DiesistdocheineabsolutunleserlicheAusgabewiemansienichtrichtigauswertenkann." bekommen.
3b.) Schreiben, die Zweite
public function writeLine(_text:String):void
{
this.text += _text + "\n";
}
Was fehlte uns beim 1.? Genau Zeilenumbrüche, das machen wir jetzt. Es ist quasi das gleiche in Grün, nur jetzt mit einem Zeilenumbruch hinten an dem Text dran. Kein Originaltext wird zerstört, dies gilt für beide Funktionen.
4.) Nun Löschen wir es wieder
public function clear():void
{
this.text = "";
}
Die letzte Methode der Klasse leert den kompletten Output, dies ist vor allem nützlich, wenn man ständig verändernde Werte von Variablen (wie die X und Y Koordinaten eines sich bewegenden Objektes) zu beobachten, ohne das ellenlange Texte entstehen. Es wird aber nicht wirklich der Text gelöscht, sondern nur der bisherige Text mit einem leeren String überschrieben. Hat aber den gleichen Effekt und frisst auch nicht mehr Speicher.
So, nun haben wir eine DebugConsole. Viel Spaß damit. Falls euch dass Tutorial gut gefallen hat, dann empfehlt mich ruhig weiter. Desweiteren gilt wie bei jedem Code, den ihr im Internet findet: Benutzung ist auf eigene Verantwortung
Sonntag, 15. Mai 2011
Impression from the board
Nach Stunden des Kampfes steht es Patt für beide Seiten. Die Lanze unter Lieutnant Blake steht der Lanze von Captain Seymore gegenüber. Blakes Lanze ist eindeutig von der gesamt Tonage unterlegen und die Scout Lanze unter Sergeant Alvarez ist zu weit entfernt. Verstärkung ist in den nächsten Stunden nicht abzusehen, beide Parteien stehen sich gegenüber. Blakes einzige Chance ist es die Stellung zu halten und zu hoffen, dass Alvarez bald zu ihnen aufschließen kann und seinem Rivalen in den Rücken fällt. Blake schaltet sein Kom ein: "Petersen, Lee, bei mir formieren!". Aus den Wäldern um Blake's Awesome herum kommen 2 weitere Mechs heraus, Sergant Petersen in seinem Grasshopper und MechWarrior Lee's Enforcer. Blake ruft sich die Karte des Gebietes nochmal auf die Anzeigen und studiert vorsichtig seine Optionen. Östlich liegt ein Berg, eine ideale Aufklärungs und Artillerie Stellung, westlich ist nur offenes Gelände. Im Norden liegt ein See mit sehr schwer passierbaren Gelände, wenn sie Seymore da hinein locken könnten, dann hätten sie den Bewegungsvorteil. "Lee, ich brauche Aufklärungsdaten über die feindlichen Kräfte. Positionen und Bewegungsrichtung, ich übertrage die letzte Bekannte Position auf ihren HUD." "Verstanden, Befehl wird ausgeführt." Lee's Enforcer verschwindet wieder im Wald, Blake sieht noch ein letztes mal auf die Karte.
MechWarrior Lee war gerade erst frisch von der Akademie und dies ist sein erster wirklicher Kampf und mit etwas Pech wohl auch schon bald sein letzter. Dieser Gedanke kreist aber nicht in Lee's Kopf, sondern nur die Erfüllung seiner Aufgabe. Um nicht gleich entdeckt zu werden schaltet Lee auf passive Radarabtastung und nähert sich dem Rand des Waldes. Auf einem kleinen Grashügel, nicht sehr weit entfernt sieht er den Umriss einer riesigen Gestalt. Er zoomt auf die Gestalt und kleine Schweißperlen bilden sich auf Lee's Stirn, denn die Gestalt ist ein Atlas, wahrscheinlich Seymore's Atlas. Hinter dem Atlas tauchen 3 weitere Umrisse auf, die eines Dragon, eines Dervish und eines Jenners. Lee zieht sich leicht in den Wald zurück um eine Sichtung zu verhindern und schaltet das Kom auf Kurzstreckenübertragung, in der Hoffnung damit eine Ortung seines Komsignals zu verhindern. "Lieutnant Blake, hier ist Lee, können sie mich empfangen?" "Lee, hier ist Lieutnant Blake, wie ist ihr Status?" "Lieutnant, ich habe Sichtkontakt mit dem Feind. Eine Lanze, 4 Mechs, schweres Chassis. Ich werde ihnen die Aufklärungsbilder senden." "Gute Arbeit Lee, suchen sie sich einen sicheren Beobachtungsposten und meiden sie jegliche Konfrontation, wir brauchen nicht noch mehr Verlusste als wir schon haben." "Verstanden."
Blake sieht sich mit faltiger Stirn die Bilder an. Der Atlas wird Probleme bereiten, wenn Alvarez sich endlich melden würde, könnten sie Versuchen den Feind in die Zange zu nehmen und hätten deutlich bessere Chancen. Genau in diesem Moment kam ein kurzes Kalken zusammen mit dem nahmen Alvarez über das Komm. Blake machte einen Komkanal auf und fing an zu senden: "Sergeant Alvarez, sind sie es, Alvarez, antworten sie!" Nichts, nur toten Stille kam über das Komm. "Alvarez, wenn sie da sind, dann melden sie sich!" Nach einer Minute, die wie eine Ewigkeit erschien, kam eine Antwort. "Lieutnant Blake, hier spricht Sergeant Alvarez. Ich kann sie hören." "Na endlich, Alvarez, wie ist ihr Status?" "Nicht so gut. Ich habe noch 2 weitere Mechs in der Lanze, alles leichtes Chassis." "Wir haben den Feind gesichtet, schweres und mittleres Chassis. Ich brauche ihre Positionsdaten." "Ich sende ihnen jetzt meine Position." Blake sah sich die Positionsdaten von Alvarez an. Alvarez befand sich nördlich von Blake, der Feind genau zwischen ihnen. "Alvarez, dies wird nicht einfach", Blake sah nochmal auf die kleine Karte auf einer seiner Anzeigen, "aber ich habe einen Plan." Blake erklärte Alvarez seinen Plan, fast schon im gleichen Moment meldete sich Lee über das Kom. "Lieutnant, der Feind hat mich bemerkt und nähert sich meiner Position, ich werde mich zurück ziehen." "Lee, fallen sie in Richtung meiner Position zurück, wir werden sie Unterstüzen, Alvarez ist endlich aufgetaucht." "Verstanden." 'Endlich', dachte sich Lee und zog sich langsam in die Wälder zurück. Blake wechselte nochmal auf Alvarez Komkanal, "Alvarez, wir wurden entdeckt, wir brauchen eine Ablenkung, sonst gibt es hier bald nichts mehr zu unterstützen."
"Sie sind im Wald. Hinter mir Formieren, wir werden sie jetzt ausräuchern." Captain Seymore schloss den Kom und setzt seinen Atlas in Bewegung. Er ist siegessicher, der Feind ist von jeglicher Verstärkung abgeschnitten und laut Aufklärungsberichten ist nicht mit großen Bedrohungen zu rechnen. "Captain Seymore, hier spricht MechWarrior Kanazawa." "Was haben sie zu berichten MechWarrior?" "Captain, in unserem Rücken wurden Feindaktivitäten berichtet, eine leichte Lanze mit 3 Mechs, haben wahrscheinlich gerade ihr Radar aktiviert." Seymore gefiel das gar nicht. Auch wenn es nur leichtes Chassis ist, sollte man nie einen Feind unterschätzen und er will in einem Zangengriff des Feindes enden. "Kanazawa, O'Rourke, kümmern sie sich um die leichte Lanze." "Verstanden Captain." "Wird gemacht." Seymore näherte sich dem Wald, hinter ihm der Dragon von Lieutenant Marks. "Marks, formation fächern, wir müssen diesen Wald etwas weiter abdecken." "Verstanden Captain, ich falle nach links ab." Seymore nähert sich der Baumlinie vorsichtig, auch wenn der Feind nicht die größte Bedrohung darstellt ist ein Kampf im Wald immer gefährlich. Aufeinmal bemerkte er zwischen den Bäumen eine Bewegung, er dreht den Torso seines Atlas und sieht einen Enforcer. 'Da bist du ja', denkt sich Seymor und visiert den Enforcer an. "Captain, Marks hier. Ich habe einen feindlichen Grasshoper, der den Wald verlässt, gesichtet, ich werde ihn verfolgen." "In Ordnung Marks, an meiner Position habe ich einen feindlichen Enforcer gesichtet." 'Und jetzt ist es vorbei für dich'. Er schaltet gerade die Laser auf, als aufeinmal von der Rechten Flanke her 3 PPC Strahlen in seinen Atlas einschlugen. 'Was zur Hölle?!' Aus dem Wald heraus folgte noch der Schuss eines Small Lasers. Seymore schlatet das Kom an. "Marks, der Feind hat schwereres Chassis als die Aufklärung berichtete, ich brauche sie hier, jetzt!"
Alvarez wies hektisch die einzelnen Bewegungskoordinaten zu. Der feindliche Dervish und der Jenner haben zwar auf ihr Ablenkungsmanöver reagiert, doch jetzt heißt es durchbrechen. Theissen, ein erfahrener Scout flankierte in seinem Cicada den Jenner und schaffte es mit seinen Lasern die beiden Lasersysteme des Jenners zu zerstören und weiter Richtung Lieutenant Blake's letzter Position zu stürmen. Alvarez schaltete das Kom an, "Jones, sie folgen Theissen." "Sir, ich kann sie doch nicht so einfach alleine lassen." "Jones! Tun sie was man ihnen sagt, ich werde hier schon alleine zurecht kommen." Auch wenn es Jones nicht gefiehl antwortete er, "Verstanden.", und folgte Theissen. Alvarez sah sich nun alleine dem Dervish und dem Jenner gegenüber. Er springt mit seinem Clint über den Dervish und schoss auf den Jenner, die AC5 traf die Pilotenkanzel und der Jenner fiel zu boden wie eine leere Dose. Nun musste er sich beeilen, der Pilot des Dervish schien ein Amateur zu sein oder zumindest ernsthafte Probleme mit seinem Mech zu haben. Alvarez sprinntet also den anderen beiden hinter her. Er erreichte gerade den kleinen Grashügel, der das Feld übersieht und inzwischen mehr verbrannte Erde als Grashügel war, als eine Explosion das Schlachtfeld erschütterte. 'Verdammt!', dachte sich Alvarez als er sah wie von Lieutenant Blakes Awesome der Fussionsreaktor explodierte.
An dieser Stelle wurde die Partie Battletech abgebrochen, da wir keine Zeit mehr hatten. Ich hoffe es war gut zu lesen images/smilies/m-smile.gif
MechWarrior Lee war gerade erst frisch von der Akademie und dies ist sein erster wirklicher Kampf und mit etwas Pech wohl auch schon bald sein letzter. Dieser Gedanke kreist aber nicht in Lee's Kopf, sondern nur die Erfüllung seiner Aufgabe. Um nicht gleich entdeckt zu werden schaltet Lee auf passive Radarabtastung und nähert sich dem Rand des Waldes. Auf einem kleinen Grashügel, nicht sehr weit entfernt sieht er den Umriss einer riesigen Gestalt. Er zoomt auf die Gestalt und kleine Schweißperlen bilden sich auf Lee's Stirn, denn die Gestalt ist ein Atlas, wahrscheinlich Seymore's Atlas. Hinter dem Atlas tauchen 3 weitere Umrisse auf, die eines Dragon, eines Dervish und eines Jenners. Lee zieht sich leicht in den Wald zurück um eine Sichtung zu verhindern und schaltet das Kom auf Kurzstreckenübertragung, in der Hoffnung damit eine Ortung seines Komsignals zu verhindern. "Lieutnant Blake, hier ist Lee, können sie mich empfangen?" "Lee, hier ist Lieutnant Blake, wie ist ihr Status?" "Lieutnant, ich habe Sichtkontakt mit dem Feind. Eine Lanze, 4 Mechs, schweres Chassis. Ich werde ihnen die Aufklärungsbilder senden." "Gute Arbeit Lee, suchen sie sich einen sicheren Beobachtungsposten und meiden sie jegliche Konfrontation, wir brauchen nicht noch mehr Verlusste als wir schon haben." "Verstanden."
Blake sieht sich mit faltiger Stirn die Bilder an. Der Atlas wird Probleme bereiten, wenn Alvarez sich endlich melden würde, könnten sie Versuchen den Feind in die Zange zu nehmen und hätten deutlich bessere Chancen. Genau in diesem Moment kam ein kurzes Kalken zusammen mit dem nahmen Alvarez über das Komm. Blake machte einen Komkanal auf und fing an zu senden: "Sergeant Alvarez, sind sie es, Alvarez, antworten sie!" Nichts, nur toten Stille kam über das Komm. "Alvarez, wenn sie da sind, dann melden sie sich!" Nach einer Minute, die wie eine Ewigkeit erschien, kam eine Antwort. "Lieutnant Blake, hier spricht Sergeant Alvarez. Ich kann sie hören." "Na endlich, Alvarez, wie ist ihr Status?" "Nicht so gut. Ich habe noch 2 weitere Mechs in der Lanze, alles leichtes Chassis." "Wir haben den Feind gesichtet, schweres und mittleres Chassis. Ich brauche ihre Positionsdaten." "Ich sende ihnen jetzt meine Position." Blake sah sich die Positionsdaten von Alvarez an. Alvarez befand sich nördlich von Blake, der Feind genau zwischen ihnen. "Alvarez, dies wird nicht einfach", Blake sah nochmal auf die kleine Karte auf einer seiner Anzeigen, "aber ich habe einen Plan." Blake erklärte Alvarez seinen Plan, fast schon im gleichen Moment meldete sich Lee über das Kom. "Lieutnant, der Feind hat mich bemerkt und nähert sich meiner Position, ich werde mich zurück ziehen." "Lee, fallen sie in Richtung meiner Position zurück, wir werden sie Unterstüzen, Alvarez ist endlich aufgetaucht." "Verstanden." 'Endlich', dachte sich Lee und zog sich langsam in die Wälder zurück. Blake wechselte nochmal auf Alvarez Komkanal, "Alvarez, wir wurden entdeckt, wir brauchen eine Ablenkung, sonst gibt es hier bald nichts mehr zu unterstützen."
"Sie sind im Wald. Hinter mir Formieren, wir werden sie jetzt ausräuchern." Captain Seymore schloss den Kom und setzt seinen Atlas in Bewegung. Er ist siegessicher, der Feind ist von jeglicher Verstärkung abgeschnitten und laut Aufklärungsberichten ist nicht mit großen Bedrohungen zu rechnen. "Captain Seymore, hier spricht MechWarrior Kanazawa." "Was haben sie zu berichten MechWarrior?" "Captain, in unserem Rücken wurden Feindaktivitäten berichtet, eine leichte Lanze mit 3 Mechs, haben wahrscheinlich gerade ihr Radar aktiviert." Seymore gefiel das gar nicht. Auch wenn es nur leichtes Chassis ist, sollte man nie einen Feind unterschätzen und er will in einem Zangengriff des Feindes enden. "Kanazawa, O'Rourke, kümmern sie sich um die leichte Lanze." "Verstanden Captain." "Wird gemacht." Seymore näherte sich dem Wald, hinter ihm der Dragon von Lieutenant Marks. "Marks, formation fächern, wir müssen diesen Wald etwas weiter abdecken." "Verstanden Captain, ich falle nach links ab." Seymore nähert sich der Baumlinie vorsichtig, auch wenn der Feind nicht die größte Bedrohung darstellt ist ein Kampf im Wald immer gefährlich. Aufeinmal bemerkte er zwischen den Bäumen eine Bewegung, er dreht den Torso seines Atlas und sieht einen Enforcer. 'Da bist du ja', denkt sich Seymor und visiert den Enforcer an. "Captain, Marks hier. Ich habe einen feindlichen Grasshoper, der den Wald verlässt, gesichtet, ich werde ihn verfolgen." "In Ordnung Marks, an meiner Position habe ich einen feindlichen Enforcer gesichtet." 'Und jetzt ist es vorbei für dich'. Er schaltet gerade die Laser auf, als aufeinmal von der Rechten Flanke her 3 PPC Strahlen in seinen Atlas einschlugen. 'Was zur Hölle?!' Aus dem Wald heraus folgte noch der Schuss eines Small Lasers. Seymore schlatet das Kom an. "Marks, der Feind hat schwereres Chassis als die Aufklärung berichtete, ich brauche sie hier, jetzt!"
Alvarez wies hektisch die einzelnen Bewegungskoordinaten zu. Der feindliche Dervish und der Jenner haben zwar auf ihr Ablenkungsmanöver reagiert, doch jetzt heißt es durchbrechen. Theissen, ein erfahrener Scout flankierte in seinem Cicada den Jenner und schaffte es mit seinen Lasern die beiden Lasersysteme des Jenners zu zerstören und weiter Richtung Lieutenant Blake's letzter Position zu stürmen. Alvarez schaltete das Kom an, "Jones, sie folgen Theissen." "Sir, ich kann sie doch nicht so einfach alleine lassen." "Jones! Tun sie was man ihnen sagt, ich werde hier schon alleine zurecht kommen." Auch wenn es Jones nicht gefiehl antwortete er, "Verstanden.", und folgte Theissen. Alvarez sah sich nun alleine dem Dervish und dem Jenner gegenüber. Er springt mit seinem Clint über den Dervish und schoss auf den Jenner, die AC5 traf die Pilotenkanzel und der Jenner fiel zu boden wie eine leere Dose. Nun musste er sich beeilen, der Pilot des Dervish schien ein Amateur zu sein oder zumindest ernsthafte Probleme mit seinem Mech zu haben. Alvarez sprinntet also den anderen beiden hinter her. Er erreichte gerade den kleinen Grashügel, der das Feld übersieht und inzwischen mehr verbrannte Erde als Grashügel war, als eine Explosion das Schlachtfeld erschütterte. 'Verdammt!', dachte sich Alvarez als er sah wie von Lieutenant Blakes Awesome der Fussionsreaktor explodierte.
An dieser Stelle wurde die Partie Battletech abgebrochen, da wir keine Zeit mehr hatten. Ich hoffe es war gut zu lesen images/smilies/m-smile.gif
Sonntag, 8. Mai 2011
Actionscript 3: Eigener MovieClip
Wer meinen Blog schon seit ein paar Einträgen liest, der weiß, dass ich Actionscript hasse aber trotzdem will ich es nochmal versuchen. Diesmal gehe ich jedoch noch methodischer vor und dass will ich mit euch teilen, desshalb wird in der nächsten Zeit für Actionscript 3 von mir ein paar Pattern, Konstrukte und Klassen kommen.
Unser heutiges Problem ist, dass wenn wir nicht mit Adobe Flash CS 5 oder ähnliche Adobe Flash IDE's arbeiten, werden wir feststellen, dass das anfertigen von Animationen als Movieclips irgendwo zwischen nervig und unmöglich liegt, ohne ständig SWF's mit der Loaderklasse zu laden und zu speichern. Desshalb werden wir heute etwas konstruieren, was zweckmäßig das gleiche wie ein MovieClip macht und selbst aber rein technisch gesehen keiner ist. Beginnen wir mit der Theorie:
Wenn wir davon ausgehen, dass wir die MovieClip Klasse immer nur dann brauchen, wenn wir Animationen abspielen wollen, dann können wir davon ausgehen, dass die MovieClip Klasse sowas ähnliches ist wie ein Array von Einzelbilder, die mit einer vorher festgelegten Verzögerung nacheinander angezeigt werden. Diese Bilder müssen wir nur noch auf unserer Leinwand darstellen. Wir brauchen also ersteinmal unsere Leinwand, hierfür kann man die Stage nehmen, ich persönlich bevorzuge aber einen Grafikkontainer, den wir auf der Stage platzieren.
1.) Grafikkontainer
Wir könnten rein theoretisch genauso gut von MovieClip selbst ableiten, ich bevorzuge hierfür jedoch Sprite, es ist also geschmacks Sache.
Jetzt wo wir unseren Grafikkontainer haben, brauchen wir unser Array für die Frames und um zu wissen, welchen Frame wir gerade darstellen einen "Zeiger", der unsere Position im Array kennt.
2.) Frames + Zeiger
_frames stellt unser Frame Array da wo wir unsere Einzelbilder für die Animation speichern.
_curframe ist unser Frame Zeiger und beinhaltet unsere derzeitige Position im Array.
Ich glaube jeder sieht hier ein Problem, das Array ist leer aber der Zeiger hat schon eine Position im Array gespeichert und würde beim abrufen Fehler verursachen, desshalb ergänzen wir unsere Klasse um eine makeFrames Funktion, die unsere einzel Frames erstellen soll.
3.) makeFrames():void
Ein einfaches Grundgerüst und wenn man hier geschickt arbeitet, dann sind alle Frames für die Animation in minimaler Zeit erstellt. Der einzige Nachteil ist, dass man wirklich jeden Frame per Hand machen muss, da standardmäßig kein Interpolieren oder ähnliches vorgesehen ist. Diese ganze Klasse entstand im Zuge der Entwicklung eines Spiels, wo die Frames der Spielfigur alle einzeln entstanden sind. Wenn ihr hier euren eigenen Algorithmus habt, hält euch nichts davon ab den zu implementieren.
So, die Frames sind alle da aber die Leinwand ist noch leer, das wollen wir jetzt ändern. Hierfür verwenden wir eine Funktion drawFrame(). Diese Funktion wird uns noch nützlich werden, wenn wir die Animation erst in Gang setzen.
4.) drawFrame():void
Der letzte Frame (noch derzeitig aktueller Frame) wird entfernt, dann wird der neue Frame berechnet und dargestellt. Man könnte jetzt Performance argumentieren, dass es doch schneller wäre einfach alle Frames zu zeichnen und nur den derzeit aktuellen sichtbar zu machen mit visible auf true und bei den anderen auf false. Ich bin aber für eine saubere Leinwand, damit existiert wirklich nur derzeit der Frame, der auch angezeigt wird und man muss nicht erst alle durchtesten ob auch wirklich alle anderen unsichtbar sind.
Jetzt wo alles gesetzt ist fehlt nur noch der Timer um die Animation zum laufen zu bringen.
5.) Der Timer
und EventHandler
und Startfunktion, die vom Benutzer aufgerufen wird.
Dies sind die Grundlagen der Klasse, die ich mir ausgedacht habe. Ihr könnt sie natürlich so machen wie ihr es wollt, ich will einfach nur einigen den Google Aufwand sparen, den ich hatte bevor ich mir das hier ausgedacht hatte. Viel Spaß damit, wie immer ist das Benutzen auf eigene Gefahr (wobei ich hier nicht wirklich das Potenzial sehe, dass euer Rechner abfackelt :P ) und ich garantiere keine 100% Funktionalität (jedenfalls funktioniert es bei mir fehlerfrei). Erweitern könnt ihr die Klasse natürlich wie ihr es wollt.
PS: Hier noch ein kleines Update, sobald ihr mit makeFrames eure Frames erstellt habt, müsst ihr noch mit this.addChild(_frames[0]) euren Startframe auf der Leinwand anzeigen, ansonsten kann es zu unvorhergesehenden Fehlern kommen, bis hin zum Absturz der Application.
Unser heutiges Problem ist, dass wenn wir nicht mit Adobe Flash CS 5 oder ähnliche Adobe Flash IDE's arbeiten, werden wir feststellen, dass das anfertigen von Animationen als Movieclips irgendwo zwischen nervig und unmöglich liegt, ohne ständig SWF's mit der Loaderklasse zu laden und zu speichern. Desshalb werden wir heute etwas konstruieren, was zweckmäßig das gleiche wie ein MovieClip macht und selbst aber rein technisch gesehen keiner ist. Beginnen wir mit der Theorie:
Wenn wir davon ausgehen, dass wir die MovieClip Klasse immer nur dann brauchen, wenn wir Animationen abspielen wollen, dann können wir davon ausgehen, dass die MovieClip Klasse sowas ähnliches ist wie ein Array von Einzelbilder, die mit einer vorher festgelegten Verzögerung nacheinander angezeigt werden. Diese Bilder müssen wir nur noch auf unserer Leinwand darstellen. Wir brauchen also ersteinmal unsere Leinwand, hierfür kann man die Stage nehmen, ich persönlich bevorzuge aber einen Grafikkontainer, den wir auf der Stage platzieren.
1.) Grafikkontainer
public class MyMovieClip extends Sprite
{
}
Wir könnten rein theoretisch genauso gut von MovieClip selbst ableiten, ich bevorzuge hierfür jedoch Sprite, es ist also geschmacks Sache.
Jetzt wo wir unseren Grafikkontainer haben, brauchen wir unser Array für die Frames und um zu wissen, welchen Frame wir gerade darstellen einen "Zeiger", der unsere Position im Array kennt.
2.) Frames + Zeiger
public class MyMovieClip extends Sprite
{
private var _frames:Array;
private var _curframe:int;
public function MyMovieClip()
{
_frames = new Array();
_curframe = 0;
}
}
_frames stellt unser Frame Array da wo wir unsere Einzelbilder für die Animation speichern.
_curframe ist unser Frame Zeiger und beinhaltet unsere derzeitige Position im Array.
Ich glaube jeder sieht hier ein Problem, das Array ist leer aber der Zeiger hat schon eine Position im Array gespeichert und würde beim abrufen Fehler verursachen, desshalb ergänzen wir unsere Klasse um eine makeFrames Funktion, die unsere einzel Frames erstellen soll.
3.) makeFrames():void
private function makeFrames():void
{
var i:int;
var grenze:int = 5; //hier kommt eure max anzahl an Frames rein
for(i = 0; i < grenze; i++)
{
//hier kommt euer Algorithmus mit dem die Frames erzeugt werden rein
}
}
Ein einfaches Grundgerüst und wenn man hier geschickt arbeitet, dann sind alle Frames für die Animation in minimaler Zeit erstellt. Der einzige Nachteil ist, dass man wirklich jeden Frame per Hand machen muss, da standardmäßig kein Interpolieren oder ähnliches vorgesehen ist. Diese ganze Klasse entstand im Zuge der Entwicklung eines Spiels, wo die Frames der Spielfigur alle einzeln entstanden sind. Wenn ihr hier euren eigenen Algorithmus habt, hält euch nichts davon ab den zu implementieren.
So, die Frames sind alle da aber die Leinwand ist noch leer, das wollen wir jetzt ändern. Hierfür verwenden wir eine Funktion drawFrame(). Diese Funktion wird uns noch nützlich werden, wenn wir die Animation erst in Gang setzen.
4.) drawFrame():void
private function drawFrame():void
{
//letzter Frame (derzeit aktueller _curframe) von der Leinwand entfernen
this.removeChild(_frames[_curframe]);
//zum neuen Frame gehen und anzeigen
_curframe++;
if(_curframe >= _frames.length)
_curframe = 0;
this.addChild(_frames[_curframe]);
}
//im Konstruktor nach Aufruf von makeFrames();
drawFrame();
Der letzte Frame (noch derzeitig aktueller Frame) wird entfernt, dann wird der neue Frame berechnet und dargestellt. Man könnte jetzt Performance argumentieren, dass es doch schneller wäre einfach alle Frames zu zeichnen und nur den derzeit aktuellen sichtbar zu machen mit visible auf true und bei den anderen auf false. Ich bin aber für eine saubere Leinwand, damit existiert wirklich nur derzeit der Frame, der auch angezeigt wird und man muss nicht erst alle durchtesten ob auch wirklich alle anderen unsichtbar sind.
Jetzt wo alles gesetzt ist fehlt nur noch der Timer um die Animation zum laufen zu bringen.
5.) Der Timer
private var _animationtmr:Timer;
//im Konstruktor
_animationtmr = new Timer(1000/60, 0); //60 FPS, unendlich viele durchläufe
und EventHandler
//Handler Funktion
private function tick(evnt:TimerEvent):void
{
//einfach nur drawFrame aufrufen und der Rest erledigt sich von allein
drawFrame();
}
//im Konstruktor nach dem anlegen des Timers
//aufruf der Funktion mit jedem Tick, Funktionsname, useCapture, priority, weakBinding
_animationtmr.addEventListener(TimerEvent.TIMER, tick, false, 0, true);
und Startfunktion, die vom Benutzer aufgerufen wird.
public function play():void
{
_animationtmr.start();
}
Dies sind die Grundlagen der Klasse, die ich mir ausgedacht habe. Ihr könnt sie natürlich so machen wie ihr es wollt, ich will einfach nur einigen den Google Aufwand sparen, den ich hatte bevor ich mir das hier ausgedacht hatte. Viel Spaß damit, wie immer ist das Benutzen auf eigene Gefahr (wobei ich hier nicht wirklich das Potenzial sehe, dass euer Rechner abfackelt :P ) und ich garantiere keine 100% Funktionalität (jedenfalls funktioniert es bei mir fehlerfrei). Erweitern könnt ihr die Klasse natürlich wie ihr es wollt.
PS: Hier noch ein kleines Update, sobald ihr mit makeFrames eure Frames erstellt habt, müsst ihr noch mit this.addChild(_frames[0]) euren Startframe auf der Leinwand anzeigen, ansonsten kann es zu unvorhergesehenden Fehlern kommen, bis hin zum Absturz der Application.
Abonnieren
Posts (Atom)