KWEngine, Teil 5: Kollisionsbehandlung

Die zwei Hitboxen der Objekte A (grün) und B (blau) überschneiden sich. Die Kollision kann mit Hilfe des „minimalen Translationsvektors“ (rot) aufgelöst werden.

Einleitung

Kollisionen werden in KWEngine auf folgende Weise stattfinden:

  • Eine GameObject-Instanz bittet die Engine, eine Liste mit den Objekten zu erstellen, mit denen die Instanz zum Zeitpunkt des aktuellen Frames kollidiert.
  • Die Engine sucht nach allen Objekten, die sich zum aktuellen Zeitpunkt in der Nähe der aufrufenden Instanz befinden und prüft dann für jedes dieser Objekte, ob es mit der aufrufenden Instanz kollidiert.
    • Wenn die Engine gebeten wurde, nur nach einer Kollision zu suchen, wird das erste Objekt zurückgegeben, für das eine Kollision festgestellt wurde. Alle nachfolgenden Kollisionen werden ignoriert.
      Sollte keine Kollision festgestellt werden, wird der Wert null zurückgegeben.
    • Wenn die Engine stattdessen gebeten wurde, alle Kollisionen zu erfassen, erstellt sie eine Liste aller Kollisionen (Intersections). Diese Liste wird der aufrufenden GameObject-Instanz als Rückgabewert zurückgegeben. Diese Liste kann leer sein, wenn keine Kollisionen festgestellt werden.
  • Der/die Programmierer/-in muss dann manuell entscheiden, wie auf die Kollisionen reagiert wird.

Wird z.B. genau eine Kollision gefunden, erhält die GameObject-Instanz diese in Form eines Intersection-Objekts. In diesem Objekt sind folgende Informationen zu finden:

  • Ein Verweis auf das Kollisionsobjekt, so dass je Objektart unterschiedlich reagiert werden kann.
  • Der „minimale Translationsvektor“ (kurz: MTV), der angibt, wie man die eigene Instanz verschieben müsste, um die Kollision rückgängig zu machen.
    In der oben abgebildeten Skizze ist der MTV als roter Pfeil eingezeichnet und würde für Objekt A (grün) andeuten, dass eine Bewegung nach rechts die Kollision „ungeschehen“ machen würde.

Bevor die eigentliche Überprüfung stattfinden kann…

…müssen die betroffenen Objekte noch so eingestellt werden, dass sie für die Kollisionsprüfung aktiviert sind:

public class GameWorld : World
{
    public override void Prepare()
    {
        Player p = new Player();
        p.SetModel("KWCube");
        p.SetPosition(+5, 0, 0);
        p.IsCollisionObject = true; // Das ist NEU!
        AddGameObject(p);

        Enemy e = new Enemy();
        e.SetModel("KWCube");
        e.SetPosition(-5, 0, 0);
        e.IsCollisionObject = true; // Das ist NEU!
        AddGameObject(e);
    }
}

Im obigen Codebeispiel werden zwei Objekte (eines vom Typ Player, das andere vom Typ Enemy) erzeugt. Bei beiden Objekten (p und e) wird die Eigenschaft IsCollisionObject auf true gesetzt. Nur dann werden beide Objekte für die Kollisionsprüfung betrachtet.


Überprüfung von genau einer Kollision

public class Player : GameObject
{
    public override void Act(KeyboardState ks, MouseState ms)
    {
        // Erst Bewegungen ausführen:
        MoveOffset(-0.1f, 0f, 0f);

        // Die Engine bitten, nach genau einer Kollision zu suchen:
        Intersection i = GetIntersection();

        // Wenn es eine Kollision gibt, dann ist i NICHT null.
        if(i != null)
        {
            // In der Kollision i ist ein Verweis auf 
            // das Kollisionsobjekt:
            GameObject collider = i.Object;

            // Das Objekt ist immer vom allgemeinen Typ 'GameObject'.
            // Es kann aber gefragt werden, welche Objektart (Klasse) 
            // tatsächlich dahinter steckt:
            if(collider is Enemy)
            {
                Console.WriteLine("Enemy berührt!");
            }

            // i wird um den MTV gebeten (ein Vector3-Objekt):
            Vector3 mtv = i.MTV;

            // Die Player-Instanz bewegt sich entlang des MTV
            // um nicht mehr mit dem Objekt zu kollidieren:
            MoveOffset(mtv);
        }
    }
}

Überprüfung aller objektbezogenen Kollisionen

Verwendet man die Methode GetIntersections() verwendet, wird eine Liste von Kollisionen (Intersection-Instanzen) zurückgegeben. Diese kann dann z.B. in einer foreach-Schleife durchlaufen werden und auf jede Kollision individuell reagiert werden:

public class Player : GameObject
{
    public override void Act(KeyboardState ks, MouseState ms)
    {
        MoveOffset(-0.1f, 0f, 0f);

        List<Intersection> intersections = GetIntersections();
        foreach(Intersection i in intersections)
        {
            GameObject collider = i.Object;
            if(collider is Enemy)
            {
                Console.WriteLine("Kollision mit Enemy");
            }

            Vector3 mtv = i.MTV;
            MoveOffset(mtv);
        }
    }
}

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.