Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Erfahren Sie, wie Sie Ihrem DirectX-Spiel herkömmliche Maus- und Tastatur-Bewegungs-/Blicksteuerungen (auch als Mouselook-Steuerelemente bezeichnet) hinzufügen.
Darüber hinaus besprechen wir die Unterstützung von Bewegungs- und Blicksteuerung für Touchgeräte. Der Bewegungscontroller wird als der untere linke Bereich des Bildschirms definiert, der sich wie eine direktionale Eingabe verhält. Der Blickcontroller ist für den Rest des Bildschirms definiert, wobei sich die Kamera auf den letzten Punkt zentriert, den der Spieler in diesem Bereich berührt hat.
Wenn dies ein unbekanntes Steuerungskonzept für Sie ist, denken Sie daran: Die Tastatur (oder das touchbasierte, richtungsweisende Eingabefeld) steuert Ihre Beine in diesem 3D-Raum und verhält sich so, als ob Ihre Beine nur in der Lage wären, sich vorwärts oder rückwärts zu bewegen oder nach links und rechts zu gehen. Die Maus (oder das Touchpad) steuert den Kopf. Sie verwenden Ihren Kopf, um in eine Richtung zu blicken – links oder rechts, nach oben oder unten oder irgendwo in dieser Ebene. Wenn in Ihrer Ansicht ein Ziel vorhanden ist, verwenden Sie die Maus, um die Kameraansicht auf diesem Ziel zu zentrieren, und drücken Sie dann die Vorwärtstaste, um zu ihr zu gelangen, oder zurück, um sich davon zu entfernen. Um das Ziel einzukreisen, würden Sie die Kameraansicht auf dem Ziel zentriert halten und gleichzeitig nach links oder rechts bewegen. Sie können sehen, wie dies eine sehr effektive Steuerungsmethode für die Navigation in 3D-Umgebungen ist!
Diese Steuerelemente werden häufig als WASD-Steuerelemente in Spielen bezeichnet, bei denen die Tasten W, A, S und D für fixierte Kamerabewegungen auf der x-z-Ebene verwendet werden, während die Maus genutzt wird, um die Kameradrehung um die x- und y-Achse zu steuern.
Ziele
- Fügen Sie Ihrem DirectX-Spiel einfache Bewegungs-/Blicksteuerungen für Maus und Tastatur sowie Touchscreens hinzu.
- Implementieren Sie eine First-Person-Kamera, die zum Navigieren in einer 3D-Umgebung verwendet wird.
Eine Notiz zu Implementierungen von Touchsteuerungen
Für Touchsteuerungen implementieren wir zwei Controller: den Bewegungscontroller, der die Bewegung in der X-Z-Ebene relativ zum Blickpunkt der Kamera steuert; und den Blickcontroller, der auf den Blickpunkt der Kamera abzielt. Unser Bewegungscontroller ordnet die WASD-Tasten der Tastatur zu, und der Blickcontroller ist der Maus zugeordnet. Für Touch-Steuerelemente müssen wir jedoch einen Bereich des Bildschirms definieren, der als Richtungstasten oder die virtuellen WASD-Schaltflächen dient, wobei der Rest des Bildschirms als Eingabebereich für die Blicksteuerung dient.
Unser Bildschirm sieht wie folgt aus.
Wenn Sie den Fingereingabezeiger (nicht die Maus!) in der unteren linken Ecke des Bildschirms bewegen, bewirkt jede Bewegung nach oben, dass sich die Kamera nach vorne bewegt. Jede Bewegung nach unten führt dazu, dass die Kamera rückwärts bewegt wird. Gleiches gilt für die linke und rechte Bewegung innerhalb des Zeigerbereichs des Bewegungscontrollers. Außerhalb dieses Bereichs wird es zu einem Blickcontroller – Sie berühren oder ziehen die Kamera dorthin, wohin sie zeigen soll.
Einrichten der grundlegenden Eingabeereignisinfrastruktur
Zunächst müssen wir unsere Kontrollklasse erstellen, die wir verwenden, um Eingabeereignisse von Maus und Tastatur zu bearbeiten. Anschließend aktualisieren wir die Kameraperspektive basierend auf diesen Eingaben. Da wir Bewegungs- und Blicksteuerungen implementieren, nennen wir es MoveLookController.
using namespace Windows::UI::Core;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Devices::Input;
#include <DirectXMath.h>
// Methods to get input from the UI pointers
ref class MoveLookController
{
}; // class MoveLookController
Als Nächstes erstellen wir einen Header, der den Zustand des Move-Look-Controllers und seiner First-Person-Kamera definiert, sowie die grundlegenden Methoden und Ereignishandler, die die Steuerungen implementieren und den Zustand der Kamera aktualisieren.
#define ROTATION_GAIN 0.004f // Sensitivity adjustment for the look controller
#define MOVEMENT_GAIN 0.1f // Sensitivity adjustment for the move controller
ref class MoveLookController
{
private:
// Properties of the controller object
DirectX::XMFLOAT3 m_position; // The position of the controller
float m_pitch, m_yaw; // Orientation euler angles in radians
// Properties of the Move control
bool m_moveInUse; // Specifies whether the move control is in use
uint32 m_movePointerID; // Id of the pointer in this control
DirectX::XMFLOAT2 m_moveFirstDown; // Point where initial contact occurred
DirectX::XMFLOAT2 m_movePointerPosition; // Point where the move pointer is currently located
DirectX::XMFLOAT3 m_moveCommand; // The net command from the move control
// Properties of the Look control
bool m_lookInUse; // Specifies whether the look control is in use
uint32 m_lookPointerID; // Id of the pointer in this control
DirectX::XMFLOAT2 m_lookLastPoint; // Last point (from last frame)
DirectX::XMFLOAT2 m_lookLastDelta; // For smoothing
bool m_forward, m_back; // States for movement
bool m_left, m_right;
bool m_up, m_down;
public:
// Methods to get input from the UI pointers
void OnPointerPressed(
_In_ Windows::UI::Core::CoreWindow^ sender,
_In_ Windows::UI::Core::PointerEventArgs^ args
);
void OnPointerMoved(
_In_ Windows::UI::Core::CoreWindow^ sender,
_In_ Windows::UI::Core::PointerEventArgs^ args
);
void OnPointerReleased(
_In_ Windows::UI::Core::CoreWindow^ sender,
_In_ Windows::UI::Core::PointerEventArgs^ args
);
void OnKeyDown(
_In_ Windows::UI::Core::CoreWindow^ sender,
_In_ Windows::UI::Core::KeyEventArgs^ args
);
void OnKeyUp(
_In_ Windows::UI::Core::CoreWindow^ sender,
_In_ Windows::UI::Core::KeyEventArgs^ args
);
// Set up the Controls that this controller supports
void Initialize( _In_ Windows::UI::Core::CoreWindow^ window );
void Update( Windows::UI::Core::CoreWindow ^window );
internal:
// Accessor to set position of controller
void SetPosition( _In_ DirectX::XMFLOAT3 pos );
// Accessor to set position of controller
void SetOrientation( _In_ float pitch, _In_ float yaw );
// Returns the position of the controller object
DirectX::XMFLOAT3 get_Position();
// Returns the point which the controller is facing
DirectX::XMFLOAT3 get_LookPoint();
}; // class MoveLookController
Unser Code enthält vier Gruppen privater Felder. Lassen Sie uns den Zweck jedes einzelnen überprüfen.
Zunächst definieren wir einige nützliche Felder, die unsere aktualisierten Informationen zur Kameraansicht enthalten.
- m_position ist die Position der Kamera (und daher der Ansichtsplan) in der 3D-Szene mit Szenenkoordinaten.
- m_pitch ist der Neigungswinkel der Kamera oder ihre Auf-Ab-Drehung um die X-Achse der Bildebene im Radiant.
- m_yaw ist das Schwenken der Kamera oder die Drehung von links rechts um die Y-Achse des Ansichtsplans in Bogenmaß.
Jetzt definieren wir die Felder, die wir zum Speichern von Informationen über den Status und die Position unserer Controller verwenden. Zunächst definieren wir die Felder, die wir für unseren touchbasierten Bewegungscontroller benötigen. (Für die Tastaturimplementierung des Bewegungscontrollers ist nichts Besonderes erforderlich. Wir lesen einfach Tastaturereignisse mit bestimmten Handlern.)
- m_moveInUse gibt an, ob der Bewegungscontroller verwendet wird.
- m_movePointerID ist die eindeutige ID für den aktuellen Bewegungszeiger. Wir verwenden sie, um zwischen dem Blickzeiger und dem Bewegungszeiger zu unterscheiden, wenn der Zeiger-ID-Wert überprüft wird.
- m_moveFirstDown ist der Punkt auf dem Bildschirm, an dem der Spieler zuerst den Zeigerbereich des Bewegungscontrollers berührt hat. Wir verwenden diesen Wert später, um eine Totzone festzulegen, die verhindert, dass winzige Bewegungen die Ansicht zum Zittern bringen.
- m_movePointerPosition ist der Punkt auf dem Bildschirm, auf dem der Spieler den Zeiger aktuell verschoben hat. Wir verwenden es, um zu bestimmen, in welche Richtung sich der Spieler bewegen wollte, indem wir es in Bezug auf m_moveFirstDownuntersuchen.
- m_moveCommand ist der letzte berechnete Befehl für den Bewegungscontroller: nach oben (vorwärts), unten (zurück), links oder rechts.
Jetzt definieren wir die Felder, die wir für unsere Blicksteuerung verwenden, sowohl die Maus- als auch die Touch-Implementierung.
- m_lookInUse gibt an, ob das Look-Steuerelement verwendet wird.
- m_lookPointerID ist die eindeutige ID für den aktuellen Blickzeiger. Wir verwenden sie, um zwischen dem Blickzeiger und dem Bewegungszeiger zu unterscheiden, wenn der Zeiger-ID-Wert überprüft wird.
- m_lookLastPoint ist der letzte Punkt in Szenenkoordinaten, der im vorherigen Frame erfasst wurde.
- m_lookLastDelta ist der berechnete Unterschied zwischen dem aktuellen m_position und m_lookLastPoint.
Schließlich definieren wir 6 boolesche Werte für die 6 Bewegungsgrade, die wir verwenden, um den aktuellen Zustand der einzelnen Bewegungsaktionen (ein oder aus) anzugeben:
- m_forward, m_back, m_left, m_right, m_up und m_down.
Wir verwenden die 6 Ereignishandler, um die Eingabedaten zu erfassen, die wir zum Aktualisieren des Zustands unserer Controller verwenden:
- OnPointerPressed. Der Spieler hat die linke Maustaste mit dem Mauszeiger auf dem Spielbildschirm gedrückt oder den Bildschirm berührt.
- OnPointerMoved. Der Spieler hat die Maus mit dem Mauszeiger auf dem Spielbildschirm bewegt oder den Fingereingabezeiger auf dem Bildschirm gezogen.
- OnPointerReleased. Der Spieler hat die linke Maustaste mit dem Mauszeiger in unserem Spielbildschirm losgelassen oder das Berühren des Bildschirms beendet.
- OnKeyDown. Der Spieler hat eine Taste gedrückt.
- OnKeyUp. Der Spieler lässt eine Taste los.
Und schließlich verwenden wir diese Methoden und Eigenschaften zum Initialisieren, Zugreifen und Aktualisieren der Zustandsinformationen der Controller.
- Initialisieren. Unsere App ruft diesen Ereignishandler auf, um die Steuerelemente zu initialisieren und sie an das Objekt CoreWindow anzufügen, das unser Anzeigefenster beschreibt.
- SetPosition. Unsere App ruft diese Methode auf, um die Koordinaten (x, y und z) unserer Steuerelemente im Szenenbereich festzulegen.
- SetOrientation. Unsere App ruft diese Methode auf, um den Neigungs- und Schwenkwinkel der Kamera festzulegen.
- get_Position. Unsere App greift auf diese Eigenschaft zu, um die aktuelle Position der Kamera im Szenenbereich abzurufen. Sie verwenden diese Eigenschaft als Methode für die Kommunikation der aktuellen Kameraposition an die App.
- get_LookPoint. Unsere App greift auf diese Eigenschaft zu, um den aktuellen Punkt abzurufen, an den die Controllerkamera gerichtet ist.
- Aktualisieren. Liest den Zustand der Bewegungs- und Blicksteuerungen aus und aktualisiert die Position der Kamera. Sie rufen diese Methode kontinuierlich aus der Hauptschleife der Anwendung auf, um die Kameracontrollerdaten und die Kameraposition im Szenenraum zu aktualisieren.
Jetzt haben Sie hier alle Komponenten, die Sie benötigen, um Ihre Bewegungs-/Blicksteuerungen zu implementieren. Lassen Sie uns diese Teile also miteinander verbinden.
Erstelle die grundlegenden Eingabeereignisse
Der Windows-Runtime-Ereignisverteiler stellt fünf Ereignisse bereit, die Instanzen der MoveLookController- Klasse behandeln sollen:
Diese Ereignisse werden im Typ CoreWindow implementiert. Es wird davon ausgegangen, dass Sie über ein CoreWindow-Objekt verfügen, mit dem Sie arbeiten können. Wenn Sie nicht wissen, wie Sie diese erhalten, lesen Sie So richten Sie Ihre C++-App für die Universal Windows-Plattform (UWP) ein, um eine DirectX-Ansicht anzuzeigen.
Wenn diese Ereignisse ausgelöst werden, während unsere App ausgeführt wird, aktualisieren die Handler die Statusinformationen der Controller, die in unseren privaten Feldern definiert sind.
Als Erstes füllen wir die Ereignishandler für Maus- und Fingereingabezeiger auf. Im ersten Ereignishandler, OnPointerPressed(), rufen wir die x-y-Koordinaten des Zeigers aus der CoreWindow ab, die unsere Anzeige verwaltet, wenn der Benutzer auf die Maus klickt oder den Bildschirm im Bereich der Ansichtssteuerung berührt.
OnPointerPressed
void MoveLookController::OnPointerPressed(
_In_ CoreWindow^ sender,
_In_ PointerEventArgs^ args)
{
// Get the current pointer position.
uint32 pointerID = args->CurrentPoint->PointerId;
DirectX::XMFLOAT2 position = DirectX::XMFLOAT2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );
auto device = args->CurrentPoint->PointerDevice;
auto deviceType = device->PointerDeviceType;
if ( deviceType == PointerDeviceType::Mouse )
{
// Action, Jump, or Fire
}
// Check if this pointer is in the move control.
// Change the values to percentages of the preferred screen resolution.
// You can set the x value to <preferred resolution> * <percentage of width>
// for example, ( position.x < (screenResolution.x * 0.15) ).
if (( position.x < 300 && position.y > 380 ) && ( deviceType != PointerDeviceType::Mouse ))
{
if ( !m_moveInUse ) // if no pointer is in this control yet
{
// Process a DPad touch down event.
m_moveFirstDown = position; // Save the location of the initial contact.
m_movePointerPosition = position;
m_movePointerID = pointerID; // Store the id of the pointer using this control.
m_moveInUse = TRUE;
}
}
else // This pointer must be in the look control.
{
if ( !m_lookInUse ) // If no pointer is in this control yet...
{
m_lookLastPoint = position; // save the point for later move
m_lookPointerID = args->CurrentPoint->PointerId; // store the id of pointer using this control
m_lookLastDelta.x = m_lookLastDelta.y = 0; // these are for smoothing
m_lookInUse = TRUE;
}
}
}
Dieser Ereignishandler überprüft, ob der Zeiger nicht die Maus ist (für dieses Beispiel, das sowohl Maus als auch Toucheingabe unterstützt) und ob er sich im Bereich des Bewegungscontrollers befindet. Wenn beide Kriterien wahr sind, wird überprüft, ob der Zeiger eben gedrückt wurde, insbesondere ob dieser Klick nicht mit einem vorherigen Bewegungs- oder Sichtinput verknüpft ist, indem getestet wird, ob m_moveInUse falsch ist. Wenn dies der Fall ist, erfasst der Handler den Punkt im Bereich des Bewegungscontrollers, an dem der Druck erfolgt ist, und setzt m_moveInUse auf "true", damit beim erneuten Aufruf dieses Handlers die Startposition des Eingabeinteraktion des Bewegungscontrollers nicht überschrieben wird. Außerdem wird die Zeiger-ID des Bewegungscontrollers auf die ID des aktuellen Zeigers aktualisiert.
Wenn der Zeiger die Maus ist oder wenn sich der Touchzeiger nicht im Bereich der Bewegungssteuerung befindet, muss er sich im Bereich der Blicksteuerung befinden. Er legt m_lookLastPoint auf die aktuelle Position fest, an der der Benutzer die Maustaste gedrückt oder berührt und gedrückt hat, das Delta zurücksetzt und die Zeiger-ID des Blickcontrollers auf die aktuelle Zeiger-ID aktualisiert. Außerdem wird der Zustand des Blickcontrollers auf "aktiv" festgelegt.
OnPointerMoved
void MoveLookController::OnPointerMoved(
_In_ CoreWindow ^sender,
_In_ PointerEventArgs ^args)
{
uint32 pointerID = args->CurrentPoint->PointerId;
DirectX::XMFLOAT2 position = DirectX::XMFLOAT2(args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y);
// Decide which control this pointer is operating.
if (pointerID == m_movePointerID) // This is the move pointer.
{
// Move control
m_movePointerPosition = position; // Save the current position.
}
else if (pointerID == m_lookPointerID) // This is the look pointer.
{
// Look control
DirectX::XMFLOAT2 pointerDelta;
pointerDelta.x = position.x - m_lookLastPoint.x; // How far did pointer move
pointerDelta.y = position.y - m_lookLastPoint.y;
DirectX::XMFLOAT2 rotationDelta;
rotationDelta.x = pointerDelta.x * ROTATION_GAIN; // Scale for control sensitivity.
rotationDelta.y = pointerDelta.y * ROTATION_GAIN;
m_lookLastPoint = position; // Save for the next time through.
// Update our orientation based on the command.
m_pitch -= rotationDelta.y; // Mouse y increases down, but pitch increases up.
m_yaw -= rotationDelta.x; // Yaw is defined as CCW around the y-axis.
// Limit the pitch to straight up or straight down.
m_pitch = (float)__max(-DirectX::XM_PI / 2.0f, m_pitch);
m_pitch = (float)__min(+DirectX::XM_PI / 2.0f, m_pitch);
}
}
Die OnPointerMoved Ereignishandlermethode wird ausgelöst, wenn der Zeiger bewegt wird (in diesem Fall, wenn ein Touchscreenzeiger gezogen wird oder der Mauszeiger mit gedrückter linker Taste verschoben wird). Wenn die Zeiger-ID mit der ID des Bewegungscontrollerzeigers übereinstimmt, handelt es sich um den Bewegungszeiger. andernfalls überprüfen wir, ob es sich um den Blickcontroller handelt, der der aktive Zeiger ist.
Wenn es sich um den Bewegungscontroller handelt, aktualisieren wir einfach die Zeigerposition. Wir aktualisieren es so lange, wie das PointerMoved Ereignis ausgelöst wird, da wir die endgültige Position mit der ersten vergleichen möchten, die wir mit dem OnPointerPressed Ereignisbehandler aufgezeichnet haben.
Wenn es sich um den Look-Controller handelt, wird es etwas komplizierter. Wir müssen einen neuen Blickpunkt berechnen und die Kamera darauf zentrieren. Dazu berechnen wir das Delta zwischen dem letzten Blickpunkt und der aktuellen Bildschirmposition und multiplizieren es mit unserem Skalierungsfaktor, den wir anpassen können, um die Blickbewegungen kleiner oder größer zu machen, abhängig von der Entfernung der Bildschirmbewegung. Mit diesem Wert berechnen wir den Neigungswinkel und das Schwenken.
Schließlich müssen wir die Bewegungs- oder Blicksteuerungsverhalten deaktivieren, wenn der Spieler aufhört, die Maus zu bewegen oder den Bildschirm zu berühren. Wir verwenden OnPointerReleased-, die wir aufrufen, wenn PointerReleased ausgelöst wird, um m_moveInUse oder m_lookInUse auf FALSE zu setzen, den Kameraschwenk zu deaktivieren und die Zeiger-ID auf Null zu setzen.
OnPointerReleased
void MoveLookController::OnPointerReleased(
_In_ CoreWindow ^sender,
_In_ PointerEventArgs ^args)
{
uint32 pointerID = args->CurrentPoint->PointerId;
DirectX::XMFLOAT2 position = DirectX::XMFLOAT2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );
if ( pointerID == m_movePointerID ) // This was the move pointer.
{
m_moveInUse = FALSE;
m_movePointerID = 0;
}
else if (pointerID == m_lookPointerID ) // This was the look pointer.
{
m_lookInUse = FALSE;
m_lookPointerID = 0;
}
}
Bisher haben wir alle Touchscreen-Ereignisse behandelt. Als Nächstes behandeln wir die Tasteneingabeereignisse für einen tastaturbasierten Bewegungscontroller.
OnKeyDown
void MoveLookController::OnKeyDown(
__in CoreWindow^ sender,
__in KeyEventArgs^ args )
{
Windows::System::VirtualKey Key;
Key = args->VirtualKey;
// Figure out the command from the keyboard.
if ( Key == VirtualKey::W ) // Forward
m_forward = true;
if ( Key == VirtualKey::S ) // Back
m_back = true;
if ( Key == VirtualKey::A ) // Left
m_left = true;
if ( Key == VirtualKey::D ) // Right
m_right = true;
}
Solange eine dieser Tasten gedrückt wird, legt dieser Ereignishandler den entsprechenden Bewegungszustand auf "true" fest.
OnKeyUp-
void MoveLookController::OnKeyUp(
__in CoreWindow^ sender,
__in KeyEventArgs^ args)
{
Windows::System::VirtualKey Key;
Key = args->VirtualKey;
// Figure out the command from the keyboard.
if ( Key == VirtualKey::W ) // forward
m_forward = false;
if ( Key == VirtualKey::S ) // back
m_back = false;
if ( Key == VirtualKey::A ) // left
m_left = false;
if ( Key == VirtualKey::D ) // right
m_right = false;
}
Und wenn die Taste losgelassen wird, setzt dieser Ereignishandler den Zustand auf "false" zurück. Wenn wir Updateaufrufen, überprüft es diese Bewegungszustände und verschenkt die Kamera entsprechend. Dies ist etwas einfacher als die Touchimplementierung!
Initialisieren Sie die Touch-Steuerelemente und den Controller-Zustand
Lassen Sie uns nun die Ereignisse verbinden und alle Controllerzustandsfelder initialisieren.
Initialisieren
void MoveLookController::Initialize( _In_ CoreWindow^ window )
{
// Opt in to receive touch/mouse events.
window->PointerPressed +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerPressed);
window->PointerMoved +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerMoved);
window->PointerReleased +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerReleased);
window->CharacterReceived +=
ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &MoveLookController::OnCharacterReceived);
window->KeyDown +=
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyDown);
window->KeyUp +=
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyUp);
// Initialize the state of the controller.
m_moveInUse = FALSE; // No pointer is in the Move control.
m_movePointerID = 0;
m_lookInUse = FALSE; // No pointer is in the Look control.
m_lookPointerID = 0;
// Need to init this as it is reset every frame.
m_moveCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );
SetOrientation( 0, 0 ); // Look straight ahead when the app starts.
}
Initialize nimmt einen Verweis auf die CoreWindow Instanz der App als einen Parameter und registriert die Ereignishandler, die von uns für die entsprechenden Ereignisse an diesem CoreWindow-entwickelt wurden. Er initialisiert die IDs des Bewegungs- und Blickrichtungszeigers, setzt den Befehlsvektor für unsere Implementierung des Touchscreen-Bewegungscontrollers auf Null und richtet die Kamera beim Start der App nach vorne aus.
Abrufen und Einstellen der Position und Ausrichtung der Kamera
Definieren wir einige Methoden zum Abrufen und Festlegen der Position der Kamera in Bezug auf den Viewport.
void MoveLookController::SetPosition( _In_ DirectX::XMFLOAT3 pos )
{
m_position = pos;
}
// Accessor to set the position of the controller.
void MoveLookController::SetOrientation( _In_ float pitch, _In_ float yaw )
{
m_pitch = pitch;
m_yaw = yaw;
}
// Returns the position of the controller object.
DirectX::XMFLOAT3 MoveLookController::get_Position()
{
return m_position;
}
// Returns the point at which the camera controller is facing.
DirectX::XMFLOAT3 MoveLookController::get_LookPoint()
{
float y = sinf(m_pitch); // Vertical
float r = cosf(m_pitch); // In the plane
float z = r*cosf(m_yaw); // Fwd-back
float x = r*sinf(m_yaw); // Left-right
DirectX::XMFLOAT3 result(x,y,z);
result.x += m_position.x;
result.y += m_position.y;
result.z += m_position.z;
// Return m_position + DirectX::XMFLOAT3(x, y, z);
return result;
}
Aktualisierung der Statusinformationen des Controllers
Jetzt führen wir unsere Berechnungen durch, mit denen die Zeigerkoordinateninformationen, die in m_movePointerPosition nachverfolgt werden, in neue Koordinateninformationen für das Koordinatensystem unserer Welt umgewandelt werden. Unsere App ruft diese Methode jedes Mal auf, wenn die Hauptschleife der App aktualisiert wird. Daher berechnen wir hier die neuen Informationen zur Blickpunktposition, die wir an die App übergeben möchten, um die Ansichtsmatrix vor der Projektion in den Viewport zu aktualisieren.
void MoveLookController::Update(CoreWindow ^window)
{
// Check for input from the Move control.
if (m_moveInUse)
{
DirectX::XMFLOAT2 pointerDelta(m_movePointerPosition);
pointerDelta.x -= m_moveFirstDown.x;
pointerDelta.y -= m_moveFirstDown.y;
// Figure out the command from the touch-based virtual joystick.
if (pointerDelta.x > 16.0f) // Leave 32 pixel-wide dead spot for being still.
m_moveCommand.x = 1.0f;
else
if (pointerDelta.x < -16.0f)
m_moveCommand.x = -1.0f;
if (pointerDelta.y > 16.0f) // Joystick y is up, so change sign.
m_moveCommand.y = -1.0f;
else
if (pointerDelta.y < -16.0f)
m_moveCommand.y = 1.0f;
}
// Poll our state bits that are set by the keyboard input events.
if (m_forward)
m_moveCommand.y += 1.0f;
if (m_back)
m_moveCommand.y -= 1.0f;
if (m_left)
m_moveCommand.x -= 1.0f;
if (m_right)
m_moveCommand.x += 1.0f;
if (m_up)
m_moveCommand.z += 1.0f;
if (m_down)
m_moveCommand.z -= 1.0f;
// Make sure that 45 degree cases are not faster.
DirectX::XMFLOAT3 command = m_moveCommand;
DirectX::XMVECTOR vector;
vector = DirectX::XMLoadFloat3(&command);
if (fabsf(command.x) > 0.1f || fabsf(command.y) > 0.1f || fabsf(command.z) > 0.1f)
{
vector = DirectX::XMVector3Normalize(vector);
DirectX::XMStoreFloat3(&command, vector);
}
// Rotate command to align with our direction (world coordinates).
DirectX::XMFLOAT3 wCommand;
wCommand.x = command.x*cosf(m_yaw) - command.y*sinf(m_yaw);
wCommand.y = command.x*sinf(m_yaw) + command.y*cosf(m_yaw);
wCommand.z = command.z;
// Scale for sensitivity adjustment.
wCommand.x = wCommand.x * MOVEMENT_GAIN;
wCommand.y = wCommand.y * MOVEMENT_GAIN;
wCommand.z = wCommand.z * MOVEMENT_GAIN;
// Our velocity is based on the command.
// Also note that y is the up-down axis.
DirectX::XMFLOAT3 Velocity;
Velocity.x = -wCommand.x;
Velocity.z = wCommand.y;
Velocity.y = wCommand.z;
// Integrate
m_position.x += Velocity.x;
m_position.y += Velocity.y;
m_position.z += Velocity.z;
// Clear movement input accumulator for use during the next frame.
m_moveCommand = DirectX::XMFLOAT3(0.0f, 0.0f, 0.0f);
}
Da wir keine ruckelige Bewegung wünschen, wenn der Spieler unseren berührungsbasierten Bewegungscontroller verwendet, legen wir einen virtuellen Toleranzbereich von 32 Pixeln Durchmesser um den Zeiger fest. Wir fügen auch Geschwindigkeit hinzu, die der Befehlswert plus eine Bewegungsgewinnrate ist. (Sie können dieses Verhalten auf Ihre Vorlieben anpassen, um die Bewegungsrate basierend auf der Entfernung, die der Zeiger im Bewegungscontrollerbereich bewegt, zu verlangsamen oder zu beschleunigen.)
Wenn wir die Geschwindigkeit berechnen, übersetzen wir auch die Koordinaten, die vom Bewegungs- und Blickcontroller empfangen werden, in die Bewegung des tatsächlichen Blickpunkts, den wir an die Methode senden, die unsere Ansichtsmatrix für die Szene berechnet. Zunächst kehren wir die x-Koordinate um, denn wenn wir mit dem Blickcontroller nach links oder rechts ziehen oder bewegen, dreht sich der Blickpunkt in die entgegengesetzte Richtung in der Szene, wie eine Kamera um ihre zentrale Achse schwingen könnte. Anschließend vertauschen wir die Y- und Z-Achsen, da eine Aufwärts-/Abwärtstastenbewegung oder ein Berühren und Ziehen (was als Y-Achsen-Bewegung verstanden wird) auf dem Bewegungscontroller als eine Kamerabewegung übersetzt werden soll, die den Blickpunkt in den oder aus dem Bildschirm bewegt (die Z-Achse).
Die endgültige Position des Blickpunktes für den Spieler ist die letzte Position plus die berechnete Geschwindigkeit, und dies ist das, was vom Renderer gelesen wird, wenn die get_Position-Methode aufgerufen wird (höchstwahrscheinlich während der Initialisierung für jeden Frame). Danach setzen wir den Bewegungsbefehl auf Null zurück.
Aktualisieren der Ansichtsmatrix mit der neuen Kameraposition
Wir können eine Szenenraumkoordinate abrufen, auf die sich unsere Kamera konzentriert und die aktualisiert wird, wann immer Sie dies Ihrer App mitteilen (zum Beispiel alle 60 Sekunden in der Hauptschleife der App). Dieser Pseudocode schlägt das Aufrufverhalten vor, das Sie implementieren können:
myMoveLookController->Update( m_window );
// Update the view matrix based on the camera position.
myFirstPersonCamera->SetViewParameters(
myMoveLookController->get_Position(), // Point we are at
myMoveLookController->get_LookPoint(), // Point to look towards
DirectX::XMFLOAT3( 0, 1, 0 ) // Up-vector
);
Glückwunsch! Sie haben in Ihrem Spiel grundlegende Bewegungs- und Blicksteuerungen sowohl für Touchscreens als auch für Tastatur- und Mauseingaben implementiert!