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.

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()
{
SetCameraPosition(25, 25, 25);
SetCameraTarget(0, 0, 0);
Player p = new Player();
p.Name = "Player #1";
p.SetScale(2);
p.SetPosition(0, 1, 0);
AddGameObject(p);
Floor f = new Floor();
f.SetScale(25, 2, 25);
f.SetPosition(0, -1, 0);
//f.SetTexture("./texturefolder/tiles.jpg"); optionale Bodentextur
AddGameObject(f);
LightObjectPoint l = new LightObjectPoint( // Ein Punktlicht wirft Licht in alle Richtungen
ShadowQuality.NoShadow // 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.SetNearFar(1, 10);
l.SetColor(1, 1, 0, 2); // Gelbes Licht mit Stärke 2
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()
{
// Die statische Methode GetMouseIntersectionPoint() projiziert
// den Mauszeiger auf die Höhe 1f der Y-Achse:
Vector3 mouseCursor3D = HelperIntersection.GetMouseIntersectionPointOnPlane(
Plane.XZ, // Wähle XZ-Ebene
1f); // Projektionshöhe auf dieser Ebene soll 1 sein
// 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 = LookAtVector;
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:

Lichttypen
1) Sonnenlicht
LightObjectSun sun = new LightObjectSun(ShadowQuality.Medium, SunShadowType.CascadedShadowMap); sun.Name = "Sun"; // Name des Objekts sun.SetPosition(25f, 25f, 25f); // Position der Sonne sun.SetTarget(0f, 0f, 0f); // Ziel der Sonnenstrahlen sun.SetFOV(25); // Wie groß ist der Durchmesser der Shadow Map? sun.SetCSMFactor(CSMFactor.Four); // Wie viel größer ist die äußere Shadow Map? sun.SetNearFar(10f, 100f); // Nur Objekte in diesem Bereich werfen Schatten sun.SetColor(1f, 1f, 1f, 2f); // Farbe (RGB) und Intensität der Sonne AddLightObject(sun);
Sonnenlicht ist eine spezielle Art von Lichtquelle – sowohl, was ihre Lichtrichtung, als auch den Schattenwurf betrifft.
- Es darf pro Welt nur ein einziges Sonnenlichtobjekt geben.
- Sonnenlichtstrahlen sind unendlich lang, daher dient das „Target“ der Sonne nur als grobe Richtlinie für die Lichtrichtung.
- Sowohl die Sonnenposition, als auch das Sonnenziel sollten sich stets mit der Spielerfigur mitbewegen. Dafür müssen Sie als Programmierer selbst sorgen.
- Der Schattenmodus für Sonnenobjekte kann „Cascaded Shadow Map“ sein, was es uns ermöglicht, auch außerhalb des definierten Sichtbereichs der Sonne (
FOV) einen 2- bis 8-mal größeren Bereich mit Schatten abzudecken. Dies wird erreicht, indem der äußere Bereich wesentlich geringere Schattenqualität verwendet und die eigentlich gewählte Qualität nur dem inneren Bereich (definiert durchSetFOV()) zusteht. In der unteren Abbildung sieht man den eigentlichen Schattensichtbereich (grün) und den hier zweimal so großen „Cascaded Shadow Map“-Bereich (rot). Im grünen Bereich (dort wo meist auch die Kamera ist) werden die Schatten mit höherer Qualität gezeichnet – außerhalb (also weiter weg von der Kamera) nur mit geringerer Qualität, was aus Sicht der Kamera dann eh nicht so stark auffällt.

2) Gerichtetes Licht
LightObjectDirectional flashlight = new LightObjectDirectional(ShadowQuality.Low); flashlight.Name = "Flashlight"; // Name des Objekts flashlight.SetPosition(5f, 2f, 5f); // Position des Lichts flashlight.SetTarget(0f, 0f, 0f); // Ziel des Lichts flashlight.SetNearFar(0.1f, 10f); // Nur Objekte in diesem Bereich werfen Schatten flashlight.SetColor(1f, 1f, 1f, 2f); // Farbe (RGB) und Intensität des Lichts AddLightObject(flashlight);
Gerichtetes Licht verhält sich wie eine Taschenlampe. Es hat einen Lichtkegel, der optional mit SetFOV() eingestellt werden kann.
2) Punktlicht
LightObjectPoint point = new LightObjectPoint(ShadowQuality.Low); point.Name = "Flashlight"; // Name des Objekts point.SetPosition(0f, 2f, 0f); // Position des Lichts point.SetNearFar(0.1f, 10f); // Nur Objekte in diesem Bereich werfen Schatten point.SetColor(1f, 1f, 1f, 2f); // Farbe (RGB) und Intensität des Lichts AddLightObject(point);
Punktlicht strahlt gleichmäßig in alle Richtungen und hat daher keine Zielsetzung (der Aufruf von SetTarget() entfällt). Auch der Aufruf von SetFOV() ist wirkungslos.

Schreibe einen Kommentar