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. :)