Bewegungs-/Blicksteuerungen für Spiele
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 Bewegungs-Look-Unterstützung für Touchgeräte, wobei der Bewegungscontroller, der als unteren linken Bereich des Bildschirms definiert ist, der sich wie eine direktionale Eingabe verhält, und der für den Rest des Bildschirms definierte Blickcontroller, wobei sich die Kamera auf der letzten Stelle befindet, die der Spieler in diesem Bereich berührt hat.
Wenn dies ein unbekanntes Steuerelementkonzept für Sie ist, denken Sie daran: Die Tastatur (oder das touchbasierte direktionale Eingabefeld) steuert Ihre Beine in diesem 3D-Raum und verhält sich so, als ob Ihre Beine nur in der Lage waren, vorwärts oder rückwärts oder links und rechts zu bewegen. Die Maus (oder der Fingereingabezeiger) steuert Den Kopf. Sie verwenden Ihren Kopf, um in eine Richtung zu suchen – 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 feste Kamerabewegungen für die X-Z-Ebene verwendet werden, und die Maus wird verwendet, 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 behandelt. und der 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 Touchsteuerelemente müssen wir jedoch einen Bereich des Bildschirms definieren, der als Richtungseingaben oder die virtuellen WASD-Schaltflächen dient, wobei der rest des Bildschirms als Eingabebereich für die Suchsteuerelemente dient.
Unser Bildschirm sieht wie folgt aus.
Wenn Sie den Fingereingabezeiger (nicht die Maus!) in der unteren linken Ecke des Bildschirms bewegen, bewegt sich jede Bewegung nach oben, damit die Kamera vorwärts bewegt wird. 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 Raums und es wird zu einem Blickcontroller – Sie berühren oder ziehen die Kamera an die Stelle, an der Sie es sehen möchten.
Einrichten der grundlegenden Eingabeereignisinfrastruktur
Zunächst müssen wir unsere Steuerelementklasse erstellen, die wir zum Behandeln von Eingabeereignissen von Maus und Tastatur verwenden, und die Kameraperspektive basierend auf dieser Eingabe aktualisieren. Da wir Bewegungs-/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 eine Kopfzeile, die den Zustand des Bewegungs-Blick-Controllers und seiner First-Person-Kamera definiert, sowie die grundlegenden Methoden und Ereignishandler, die die Steuerelemente 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 die Drehung nach oben um die X-Achse des Sichtplans in Bogenmaß.
- 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 einen inaktiven Bereich festzulegen, um winzige Bewegungen vor dem Jittern der Ansicht zu bewahren.
- m_movePointerPosition ist der Punkt auf dem Bildschirm, auf dem der Spieler den Zeiger aktuell verschoben hat. Wir verwenden es, um zu bestimmen, welche Richtung der Spieler bewegen wollte, indem wir ihn relativ zu m_moveFirstDown untersuchen.
- 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 unseren Blickcontroller verwenden, sowohl die Maus- als auch die Touchimplementierung.
- m_lookInUse gibt an, ob das Erscheinungsbild-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 hat eine Taste losgelassen.
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 CoreWindow-Objekt 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 Blickcontroller und aktualisiert die Kameraposition. Sie rufen diese Methode kontinuierlich aus der Hauptschleife der App auf, um die Kameracontrollerdaten und die Kameraposition im Szenenbereich 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.
Erstellen der grundlegenden Eingabeereignisse
Der Windows-Runtime-Ereignisverteiler stellt fünf Ereignisse bereit, die Instanzen der MoveLookController-Klasse behandeln sollen:
Diese Ereignisse werden für den CoreWindow-Typ 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 eine App abrufen, erfahren Sie, wie Sie Ihre Universelle Windows-Plattform (UWP)-C++-App so einrichten, dass eine DirectX-Ansicht angezeigt wird.
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 dem CoreWindow-Element ab, das unsere Anzeige verwaltet, wenn der Benutzer auf die Maus klickt oder den Bildschirm im Bereich des Blickcontrollers 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 gerade gedrückt wurde, insbesondere, ob dieser Klick nicht mit einer vorherigen Verschiebungs- oder Blickeingabe 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, in dem die Taste erfolgt ist, und legt m_moveInUse auf "true" fest. Wenn dieser Handler erneut aufgerufen wird, wird die Startposition der Eingabeinteraktion des Bewegungscontrollers nicht überschrieben. Außerdem wird die Zeiger-ID des Bewegungscontrollers auf die ID des aktuellen Zeigers aktualisiert.
Wenn der Zeiger die Maus ist oder sich der Fingereingabezeiger nicht im Bereich des Bewegungscontrollers befindet, muss er sich im Bereich des Blickcontrollers 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);
}
}
Der OnPointerMoved-Ereignishandler wird immer ausgelöst, wenn der Zeiger bewegt wird (in diesem Fall, wenn ein Touchscreenzeiger gezogen wird oder der Mauszeiger verschoben wird, während die linke Taste gedrückt 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, solange das PointerMoved-Ereignis ausgelöst wird, da wir die endgültige Position mit dem ersten vergleichen möchten, den wir mit dem OnPointerPressed-Ereignishandler erfasst haben.
Wenn es sich um den Blickcontroller handelt, sind die Dinge etwas komplizierter. Wir müssen einen neuen Blickpunkt berechnen und die Kamera darauf zentrieren, sodass wir das Delta zwischen dem letzten Blickpunkt und der aktuellen Bildschirmposition berechnen und dann mit unserem Skalierungsfaktor multiplizieren, was wir optimieren können, um die Blickbewegungen kleiner oder größer relativ zum Abstand der Bildschirmbewegung zu machen. Mit diesem Wert berechnen wir den Neigungswinkel und das Schwenken.
Schließlich müssen wir das Bewegungs- oder Blickcontrollerverhalten deaktivieren, wenn der Spieler nicht mehr mit der Maus bewegt oder den Bildschirm berührt. Wir verwenden "OnPointerReleased", den wir aufrufen, wenn PointerReleased ausgelöst wird, um m_moveInUse oder m_lookInUse auf FALSE festzulegen und die Kameraverschiebungsbewegung 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 Touchscreenereignisse 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 der Schlüssel losgelassen wird, legt dieser Ereignishandler ihn auf "false" fest. Wenn "Update" aufgerufen wird, überprüft er diese Richtungsverschiebungszustände und verschenkt die Kamera entsprechend. Dies ist etwas einfacher als die Touchimplementierung!
Initialisieren der Touchsteuerelemente und des Controllerzustands
Lassen Sie uns nun die Ereignisse verbinden und alle Controllerzustandsfelder initialisieren.
Initialize
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 verwendet einen Verweis auf die CoreWindow-Instanz der App als Parameter und registriert die ereignishandler, die wir für die entsprechenden Ereignisse in diesem CoreWindow entwickelt haben. Er initialisiert die IDs des Bewegungs- und Blickzeigers, legt den Befehlsvektor für die Implementierung des Bewegungscontrollers auf Null fest und legt die Kamera beim Start der App direkt voraus.
Abrufen und Festlegen 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;
}
Aktualisieren der Controllerstatusinformationen
Jetzt führen wir unsere Berechnungen durch, mit denen die in m_movePointerPosition nachverfolgten Zeigerkoordinateninformationen in neue Koordinateninformationen in unserem Weltkoordinatensystem konvertiert werden. Unsere App ruft diese Methode jedes Mal auf, wenn die Haupt-App-Schleife 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 Jittery-Bewegung wünschen, wenn der Spieler unseren touchbasierten Bewegungscontroller verwendet, legen wir eine virtuelle inaktive Zone um den Zeiger mit einem Durchmesser von 32 Pixeln 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 umkehren wir die x-Koordinate, da wenn wir mit dem Blickcontroller auf die linke oder rechte Maustaste ziehen, der Blickpunkt in die entgegengesetzte Richtung in der Szene gedreht wird, da eine Kamera über die zentrale Achse schwingt. Anschließend tauschen wir die Y- und Z-Achsen aus, da eine Aufwärts-/Abwärtstastenbewegung (als Y-Achsenverhalten gelesen) auf dem Bewegungscontroller in eine Kameraaktion übersetzt werden soll, die den Blickpunkt in den oder aus dem Bildschirm bewegt (die Z-Achse).
Die endgültige Position des Blickpunkts für den Spieler ist die letzte Position plus die berechnete Geschwindigkeit, und dies ist, was vom Renderer gelesen wird, wenn die get_Position-Methode aufgerufen wird (höchstwahrscheinlich während des Setups für jeden Frame). Danach setzen wir den Bewegungsbefehl auf Null zurück.
Aktualisieren der Ansichtsmatrix mit der neuen Kameraposition
Wir können eine Szenenbereichkoordinate abrufen, auf die sich unsere Kamera konzentriert und aktualisiert wird, wenn Sie Ihrer App dies mitteilen (z. B. alle 60 Sekunden in der Haupt-App-Schleife). 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
);
Herzlichen Glückwunsch! Sie haben grundlegende Bewegungs-/Blicksteuerungen für Touchscreens und Tastatur-/Mauseingabesteuerungen in Ihrem Spiel implementiert!