Konzepte für 3D-Spiele (Umsetzung der Steuerung)

Aus Spieleprogrammierer-Wiki
Wechseln zu: Navigation, Suche
Achtung! Dieser Artikel behandelt nicht Vektoren im Detail! Er behandelt nur ihren Einsatz zur Umsetzung der Steuerung. Für mehr Informationen über Vektoren, siehe den Wikipedia-Eintrag.

Bitte beachte, dass dieser Artikel noch unvollständig ist! Hilf mit, ihn fertigzustellen.
Näheres dazu findest du ggf. auf der Diskussionsseite. Wenn du der Meinung bist, dass der Artikel vollständig ist, kannst du diesen Hinweis entfernen.

Inhaltsverzeichnis

Die Verwaltung der Koordinaten und Richtungen

In 3D-Spielen werden die Koordinaten (sprich, die Position des Spielers) mittels 3-dimensionalen Vektoren verwaltet. Die Blickrichtung wird meist anhand Fließkommazahlen (floats) für die Drehung an der X-Achse ("Hoch-Runter"), Y-Achse ("Links-Rechts") verwaltet. Hier folgt eine mögliche Gestaltung der Klassen in C++ für die Verwaltung von Koordinaten und der Blickrichtung (Implementierung am Ende).

Klassen-Struktur (C++)

// Klasse für die Verwaltung von 3-dimensionalen Koordinaten
class vektor_3
{
public:
	vektor_3(void);	// Standard-Konstruktor
	vektor_3(float X, float Y, float Z);	// Konstruktor mit Werteübergabe
 
	vektor_3 operator * (float faktor);
	vektor_3 operator / (float dividend);
	vektor_3 operator + (vektor_3 v);
	vektor_3 operator - (vektor_3 v);
 
	float x, y, z;
};
 
class blickRichtung
{
public:
	blickRichtung(void);
	blickRichtung(float xAxisRotation, yAxisRotation);
 
	vektor_3 toVektor(void);
 
	// xrot = Vertikale Komponente, yrot = Horizontale Komponente, Werte in Grad
	float xrot, yrot;
};

Berechnungen und mathematische Zusammenhänge

Wie sich aus dem Satz des Pythagoras bzw. Sinus- und Kosinussatz herleiten lässt, ist
§x = \sin(\alpha)§
§z = \cos(\alpha)§
§y = \sin(\beta)§
wobei §\alpha§ = blickRichtung::yrot, §\beta§ = blickRichtung::xrot, x, y und z sind die Komponenten von vektor_3.
Diese Vorgehensweise hat jedoch noch einen Denkfehler, welcher sich leicht beweisen und lösen lässt:

Problem mit der X- und Z-Achse

Angenommen, die vertikale Komponente (also blickRichtung::xrot) wäre 85.f, die horizontale Komponente (blickRichtung::yrot) wäre 90.f. Dann wäre
§\alpha = 90§
§\beta = 85§
§x = \sin(\alpha) = 1.0§
§z = \cos(\alpha) = 0.0§
§y = \sin(\beta) = 0.996§.
Vergleicht man diesen Vektor nun mit dem erwarteten, fast vertikalen Vektor, der minimal zur X-Achse geneigt ist, so stellt man fest, dass der X-Wert unseres Vektors zu groß ist. Der X-Wert müsste eigentlich bei ca. 0.1 liegen. Wäre y=0, so müsste x mit dem Faktor 1 multipliziert werden. Wäre y = 1, so müsste x mit 0 multipliziert werden. Daraus ergibt sich, dass man den X- und Z-Wert jeweils mit dem Kosinus von blickRichtung::yrot multiplizieren muss (Kugelkoordinaten).

Mit dem jeetzigen Wissen sind wir nun so weit, endlich die Methode blickRichtung::toVektor zu implementieren:

Implementierung von toVektor (C++)

vektor_3 blickRichtung::toVektor(void)
{
	// Zur Umrechnung Grad->Radianten
	// PS.: piover180 = (0.017453292519943295)
	const float piover180 = M_PI / 180;
	return vektor_3(
		sin(yrot*piover180)*cos(xrot*piover180),
		sin(xrot*piover180),
		cos(yrot*piover180)*cos(xrot*piover180));
}

Kombinieren des Wissens auf die Steuerung

Nun, da wir sogar Vektoren aus bloßen Richtungen berechnen können, gehen wir einen Schritt weiter: zum Kombinieren des jetzigen Wissens auf den Sachverhalt der Steuerung.

Bewegung

Bei der Bewegung gibt es vier mögliche Richtungen, in die sich der Spieler Bewegen kann: Links, Rechts, Vorne, Hinten.
Beginnen wir mit der leichtesten Richtung: vorwärts. Da wir ja unsere Richtung, in die wir gucken, einfach per blickRichtung::toVektor berechnen können, folgt:

if(keys['W'])
{
	blickRichtung angles = dir;
	pos += angles.toVektor() * frameTime;
}

Nun Rückwärts:

if(keys['S'])
{
	blickRichtung angles=dir;
	angles.yrot += 180.f;	// Wir wollen ja die entgegengesetzte Richtung
	pos += angles.toVektor() * frameTime;
}

Nun gehen wir bei der Rechts-/ Linksbewegung nach dem selben Verfahren vor:

if(keys['D'])
{
	blickRichtung angles = dir;
	angles.yrot +=90.f;
	pos += angles.toVektor() * frameTime;
}
if(keys['A'])
{
	blickRichtung angles = dir;
	angles.yrot -= 90.f;
	pos += angles.toVektor() * frameTime;
}

Umsehen

Nun, da wir nun mittels gedrehten Winkeln Vektoren zur Bewegung erstellen können, ist es ein Leichtes, damit auch Kamerasteuerung umzusetzen: Bewegt man die Maus nach rechts, soll die Kamera auch nach rechts drehen (das Gleiche natürlich auch für die anderen Richtungen). Dafür nimmt man erst die Cursor-Position, zieht den Mittelpunkt des Fensters davon ab (man hat also die Entfernung zur Fenstermitte). Dann dreht man die Blickrichtung des Spielers / der Kamera (vom Typ blickRichtung) um den Wert der Entfernung der Maus von der Fenstermitte drehen. Das könnte dann ungefähr so aussehen:


void View(HWND wnd)
{
	POINT mouse;
 
	// Mausposition ermitteln
	GetCursorPos(&mouse);
 
	RECT window;
 
	// Fensterdimensionen ermitteln
	GetWindowRect(wnd, &window);
 
	int w = window.right - window.left;
	int h = window.bottom - window.top;
 
	int midX = w/2;
	int midY = h/2;
 
	mouse.x -= window.left;
	mouse.y -= window.top;
 
	int dx = mouse.x - midX;
	int dy = mouse.y - midY;
 
	// dir: Blickrichtung, DEGREES_PER_PIXEL: Faktor
	// für Empfindlichkeit
	dir.yrot += dx * DEGREES_PER_PIXEL;
	dir.xrot += dy * DEGREES_PER_PIXEL;
 
	SetCursorPos(window.left + midX, window.top + midY);
 
	// Beachte: Dies ist nur eine "Pseudo-Funktion", soll nur
	// die Verwendung veranschaulichen.
	Graphics::LookAt(position, position + dir.toVektor());
}
Meine Werkzeuge
Namensräume
Varianten
Aktionen
Navigation
Werkzeuge