KWEngine, Teil 8: Mauszeigerinteraktionen und Lichter

Wird das Spielgeschehen in einer Szene aus der Vogelperspektive oder in der isometrischen Ansicht gezeigt, benötigen Sie eventuell eine Übersetzung der aktuellen Mauszeigerkoordinaten in die tatsächlichen 3D-Koordinaten der Spielwelt.

Vom mathematischen Blickwinkel aus betrachtet, ist dies ein gut lösbares Problem. Die eigentliche Schwierigkeit besteht darin, die 2D-Mauszeigerposition in eine 3D-Position zu übersetzen, die für den Betrachter nachvollziehbar ist und somit auch als sinnvoll erachtet wird. Denn schließlich muss man als Programmierer entscheiden, welchen Wert der Mauszeiger auf der dritten Achse hat, obwohl nur Informationen zu den anderen zwei Achsen zur Verfügung stehen.

Quelle: wikipedia.org

In der obigen Abbildung sind die drei Achsen und die sich dadurch ergebenden Blickebenen eingezeichnet.

  • Aus der Vogelperspektive (die Kamera blickt von oben entlang der y-Achse nach unten) sieht die Kamera lediglich die rot eingezeichnete Ebene. In diesem Fall bildet die x-Achse die Links-/Rechts-Bewegungen und die z-Achse die Oben-/Unten-Bewegungen ab.
    Die aus Kamerasicht nicht sichtbare Achse ist die y-Achse – sie steht in diesem Fall für Bewegungen nach vorne und nach hinten. Es kann aber auch sein, dass die Spielfigur einen Sprung entlang dieser Achse vollzieht.
  • In der KWEngine-Standardeinstellung sieht man hingegen die blaue Ebene, so dass die x-Achse für Links-/Rechtsbewegungen und die y-Achse für Oben-/Unten-Bewegungen (z.B. Sprünge) steht.
    Die aus Kamerasicht nicht sichtbare Achse ist die z-Achse – sie steht für Bewegungen nach vorne und nach hinten.

Für den Programmierer ist es nun wichtig zu entscheiden, wo der Mauszeiger auf der jeweils unsichtbaren Achse liegt.


In der folgenden Spielszene wird eine Art isometrische Ansicht simuliert, indem die Kameraposition wie folgt festgelegt wird:

SetCameraPosition(25, 25, 25);
SetCameraTarget(0, 0, 0);

Weiterhin werden in der GameWorld-Klasse eine Fläche für den Boden, sowie ein Player– und Lichtobjekt benötigt:

public class GameWorld : World
{
    public override void Prepare()
    {
        DebugShowCoordinateSystem = true;
        SetCameraPosition(25, 25, 25);
        SetCameraTarget(0, 0, 0);

        Player p = new Player();
        p.Name = "Player #1";
        p.SetModel("KWCube");
        p.SetScale(2);
        p.SetPosition(0, 1, 0);
        AddGameObject(p);

        Floor f = new Floor();
        f.SetModel("KWCube");
        f.SetScale(25, 2, 25);
        f.SetPosition(0, -1, 0);
        //f.SetTexture(@".\textures\tiles.jpg"); optionale Bodentextur
        AddGameObject(f);

        LightObject l = new LightObject(
                LightType.Point, // Licht strahlt in alle Richtungen
                false            // Licht wirft keine Schatten
            );
        l.Name = "Player #1 Light";

        // Lichtschein verbleibt über ca. 10 Längeneinheiten.
        // Der erste Parameter gibt an, wie weit Objekte mindestens 
        // von der Lichtquelle entfernt sein müssen, damit Schatten 
        // geworfen werden. Wirft das Licht keine Schatten, hat der
        // erste Parameter keinen Effekt.
        l.SetNearAndFarBounds(1, 10); 
        l.SetColor(1, 1, 0, 5);      // Gelbes Licht mit Stärke 5
        AddLightObject(l);
    }
}

In der Player-Klasse wird nun für die folgenden Dinge gesorgt:

  • Die Mauszeigerposition wird auf die Höhe des aktuellen Player-Objekts projiziert.
  • Das Player-Objekt dreht sich in die Richtung des Mauszeigers.
  • Das Licht wird anschließend immer „vor die Nase“ des Player-Objekts gesetzt.
public class Player : GameObject
{

    public override void Act(KeyboardState ks, MouseState ms)
    {
        // Die statische Methode GetMouseIntersectionPoint() projiziert
        // den Mauszeiger auf die Höhe 1f der Y-Achse:
        Vector3 mouseCursor3D = HelperIntersection.GetMouseIntersectionPoint(
            ms,      // Aktuelle Mauszeigerdaten
            Plane.Y, // Wähle y-Achse als dritte Achse
            1f);     // Projektionshöhe entlang dieser Achse ist 1

        // Dem Spielerobjekt wird befohlen, sich in Richtung
        // dieser Position zu drehen:
        TurnTowardsXZ(mouseCursor3D);

        // Ermittle den aktuellen Blickrichtungsvektor des Spielers
        // und hole die Referenz auf das Lichtobjekt:
        Vector3 lookAtVector = GetLookAtVector();
        LightObject l = CurrentWorld.GetLightObjectByName("Player #1 Light");

        // Die neue Position des Lichts ergibt sich aus 
        // der aktuellen Spielerposition plus einem Vielfachen 
        // des aktuellen Blickrichtungsvektors:
        Vector3 newLightPos = Position + lookAtVector * 2.5f;
        l.SetPosition(newLightPos);
    }
}

Das Ergebnis sieht wie folgt aus:

Schreibe einen Kommentar

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