Dienstag, 28. Juni 2011

Actionscript 3: Kollisionserkennung

Nun kommen wir mal, nachdem ich doch ziemlich beschäftigt war, zu einem der interessantesten und doch sehr schwierigen Themen, Kollisionserkennung. Die Theorie ist ja doch recht simpel, trivial ausgedrückt: Wenn 2 Objekte kollidieren, dann sollen sie irgendwas tun. Etwas komplizierter wird es nun, wenn man sich fragt: Wann kollidieren 2 Objekte mit einander?
Wieder trivial: Wenn die x- und y-koordinaten übereinander liegen. Frage: Welche x- und y-koordinaten?

Langsam wird einem bewusst, dass Kollision doch schwieriger ist als man es zuerst vermutet. Gerade bei Actionscript gibt es ziemlich viele Tutorials für Kollision, die alle mehr oder weniger funktionieren aber auch früher oder später über die Kollision mit unregelmäßigen Objekten stolpern. Hier würde ich eine Methode, die ich mal in einem Spielprogrammierungsbuch gelesen habe, empfehlen: Man teilt den Körper in einzelne Kollisionsquadrate ein. Kümmern wir uns aber nun um eine Methode, die ich von den bestehenden ausgehend entwickelt habe, die bei einer Stage mit vielen Objekten sehr effektiv arbeitet.

Theorie:
  • Kollisionsobjekte sind in einem eigenen Array gespeichert
  • zu jedem Kollisionsobjekt gibt es ein extra Objekt, dass alle Positionsdaten enthält
  • wir prüfen erst grob etwas kollidiert oder nicht
  • wenn es eine Kollision gibt, dann wird die Kollision fein geprüft, ob die Kollision auch wirklich stattfindet

Praxis:

Array mit Kollisionsobjekten:

var _stage:Array
Ein simples Array, dass alle nötigen Kollisionsobjekte beinhaltet.


Kollisionsdaten Objekt:

class StageDatas
{
private var _x:int;
private var _y:int;
private var _index:int;

public function StageDatas(index:int, x:int, y:int)
{
_x = x;
_y = y;
_index = index;
}

public function get x():int
{
return _x;
}

public function get y():int
{
return _y;
}

public function get index():int
{
return _index;
}

public function set x(x:int):void
{
_x = x;
}

public function set y(y:int):void
{
_y = y;
}

public function set index(index:int):void
{
_index = index;
}
}
Dies ist eine einfache Speicherklasse, die x-Position, y-Position ausgehend vom linken oberen Bildschirmpunkt speichert. index speichert den Index des Objektes innerhalb des Arrays mit den Kollisionsobjekten (_stage), damit nicht immer wieder neu danach gesucht werden muss, bzw. damit man die Daten schnell wieder finden kann.

Grobe Kollisionskontrolle

var hitObject:Sprite;
var data:StageDatas;
In hitObject wird unser aktuelles Testsprite rein geladen, in data finden wir alle nötigen angaben zu dem Sprite, da die Stage keine Positionsdaten speichert

for(var i:int = 0; i < _stage.length; i++)
{
   hitObject = _stage[i];
   if(!_activeObject.LastHit.hitTestPoint(_activePic.x + wOff,
       _activePic.y + hOff, true) || 
      !_activeObject.LastHit.hitTestPoint(_activePic.x +
       _activePic.width + wOff, _activePic.y + hOff,
       true))
   {
       //hier euren Code, wenn keine Kollision stattfindet
   }
   else
   {
      //hier kommt noch mehr Code
   }
}
Das erscheint jetzt etwas kompliziert. Die Idee selbst ist einfach, ich überprüfe alle möglichen HitObjekte gegen ein aktives Objekt, dass selber nicht im HitArray (_stage) ist. Somit verhindere ich Fehler durch eine Kollision mit sich selbst. hitObject speichert unser zu testendes Objekt und data die dazugehörigen Daten. _activeObject ist das Hauptobjekt, dass Kollidieren soll und wurde von mir extra definiert, ich werde euch den Aufbau ein anderes mal erklären, wichtig ist derzeit, dass es das letzte Objekt kennt, mit dem es kollidiert hat, das ist _activeObject.LastHit. Damit will ich Fehler vorbeugen, die entstehen können, wenn er auf das Objekt, mit dem er schon interagiert hat wieder interagiert obwohl es nicht notwendig ist. Zuerst teste ich ob die Kollision mit diesem Objekt überhaupt noch stattfindet. Für das _activeObject werden auch noch höhen und weiten Offset mit berücksichtigt.


Feine Kollisionsprüfung

else
{

   for(var i:int = 0; i < _stage.length; i++)
   {
      hitObject = _stage[i];
      if(_activeObject.LastHit != hitObject)
      {
         data = _stageData[i];
         if((_activeObject.LeftCollision.x == data.x + hitObject.width ||
             _activeObject.LeftCollision.x + leftOff == data.x + hitObject.width) &&
             _activeObject.LeftCollision.y >= data.y && _player.dx < 0)
         {
            //hier der Code
         }
         else if((_activeObject.RightCollision.x == data.x ||
                  _activeObject.RightCollision.x + rightOff == data.x) &&
                  _activeObject.RightCollision.y >= data.y && _activeObject.dx > 0)
         {
            //hier der Code
         }        
      }
   }
}
Die Feinabtastung ist etwas einfacher, da letztendlich nur auf Kollision gegen einzel Zonen am Objekt getestet werden. In diesem einfachen Beispiel kennt mein Objekt nur die Rechte- und die Linkekollisionszone. Gegen diese wird getestet, dabei wird darauf geachtet, dass nur immer gegen eine der beiden getestet und nicht gegen beide gleichzeitig, ansonsten kann es zu fehlern kommen. Dieser Code muss natürlich erweitert werden, wenn es mehrere Zonen gibt, hier lohnt sich sogar schon ein Array, wenn die Menge zunimmt und einfach das Array durchtesten.

Damit haben wir uns eine einfache Kollisionserkennung gebaut. Wie immer ohne Funktionsgarantie und auf eigene Verantwortung. Beim nächsten mal werde ich für euch eine Playerklasse aufziehen.

Wie immer viel Spaß damit.

Samstag, 4. Juni 2011

Unterrepräsentiertes Genre: SciFi

Wisst ihr was mich stört. Dass das Science Fiction Genre nur sehr schwach repräsentiert ist. Vielleicht bin ich nur nie in den richtigen Buchläden gewesen aber bisher habe ich festgestellt, dass SciFi gerne in ein Regal mit Fantasy geordnet ist und dann noch nur zwei bis drei Bücher sind. Ich übertreibe jetzt zwar etwas aber es ändert nichts am Kernproblem: Für eine komplette Wand Fantasy bekomme ich maximal ein kleines Regal Science Fiction. Warum? Warum kann ich mir tausendmal das gleiche Fantasy Buch nur mit anderen Titel kaufen aber wenn ich Science Fiction haben will, dann bekomme ich nur Perry Rhodan, Star Wars und mit viel Glück auch mal Star Trek oder Warhammer 40000. Das stört mich, es fehlt die Abwechslung. Ich weiß noch wie ich früher in den Regalen sowas gesehen habe wie: Neuromancer, Darkstar, Battletech: Dark Age, Jenseits des Universums, Letztes Kommando, etc.

Wieso gibt es so wenig neue Bücher in dem Genre? Auf der anderen Hand wuchern (schlechte) Twilight Kopien (und das will schon was heißen) wie Pilze überall und verdrängen den ganzen Rest. Bei Fantasy hat man ja noch Glück, dass kompetente Schreiber sich in bisherige Szenarien einreihen und dort die Welten ausbauen und stilvoll erweitern. Man findet dann doch noch mal ein Terry Pratchet, den man noch nicht hat oder eine interessante Geschichte über Schwertkampf und Zauberei oder übernatürliche Reisen durch die Welten eines kompletten Multiversums. Was finde ich beim Science Fiction: den 999999999 Band der Perry Rhodan Reihe. Hier muss ich sagen, dass ich noch nie Perry Rhodan gelesen habe, primär weil es nicht interessiert und will desshalb keine Wertung über die Qualität der Reihe abgeben. Heute habe ich dann noch ein Buch gesehen, dass in der Welt von Metro 2033 spielt und nicht vom Metro Autor ist. Das war dann doch schon ein bisschen was frisches neben den immer gleichen Büchern.

Meine Frage ist jetzt: Ist es wirklich so schwer Science Fiction zu schreiben oder ist es derzeit nur nicht einträglich? Ich respektiere schon die Autoren, die komplette Universen mit interessanten Welten und weitreichenden unbekannten Wundern erstellen können und liebe desshalb auch so sehr solche Geschichten. Vielleicht sollte ich selbst eine schreiben, nur um selbst mein Problem zu lösen. Wisst ihr was, ich glaube ich mache das und werde euch dran teilhaben lassen.

Bis dahin, bereitet euch auf etwas besonderes vor, denn ich habe absolut keine Ahnung wie man ordentlich eine Geschichte schreibt. :)

Samstag, 28. Mai 2011

Remembering: Arcanum: Of Steamworks and Magick Obscura

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?

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.

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

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