Touchsteuerungen für Spiele
Erfahren Sie, wie Sie Ihrem Universelle Windows-Plattform (UWP)-C++-Spiel mit DirectX grundlegende Touchsteuerelemente hinzufügen. Wir zeigen Ihnen, wie Sie touchbasierte Steuerelemente hinzufügen, um eine Kamera mit fester Ebene in einer Direct3D-Umgebung zu verschieben, in der das Ziehen mit einem Finger oder Eingabestift die Kameraperspektive verschiebt.
Sie können diese Steuerelemente in Spiele integrieren, in denen der Spieler zum Scrollen oder Schwenken über eine 3D-Umgebung ziehen soll, z. B. eine Karte oder ein Spielfeld. Beispielsweise können Sie in einem Strategie- oder Puzzlespiel diese Steuerelemente verwenden, damit der Spieler eine Spielumgebung anzeigen kann, die größer als der Bildschirm ist, indem Sie nach links oder rechts schwenken.
Beachten Sie, dass unser Code auch mit mausbasierten Verschiebungssteuerelementen funktioniert. Die zeigerbezogenen Ereignisse werden von den Windows-Runtime-APIs abstrahiert, sodass sie touch- oder mausbasierte Zeigerereignisse verarbeiten können.
Ziele
- Erstellen Sie ein einfaches Touch-Drag-Steuerelement zum Verschieben einer Kamera mit fester Ebene in einem DirectX-Spiel.
Einrichten der grundlegenden Infrastruktur für Touchereignisse
Zunächst definieren wir unseren grundlegenden Controllertyp CameraPanController in diesem Fall. Hier definieren wir einen Controller als abstrakte Idee, den Satz von Verhaltensweisen, die der Benutzer ausführen kann.
Die CameraPanController-Klasse ist eine regelmäßig aktualisierte Sammlung von Informationen zum Zustand des Kameracontrollers und bietet eine Möglichkeit für unsere App, diese Informationen aus der Aktualisierungsschleife abzurufen.
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 CameraPanController
{
}
Als Nächstes erstellen wir einen Header, der den Zustand des Kameracontrollers definiert, sowie die grundlegenden Methoden und Ereignishandler, die die Interaktionen des Kameracontrollers implementieren.
ref class CameraPanController
{
private:
// Properties of the controller object
DirectX::XMFLOAT3 m_position; // the position of the camera
// Properties of the camera pan control
bool m_panInUse;
uint32 m_panPointerID;
DirectX::XMFLOAT2 m_panFirstDown;
DirectX::XMFLOAT2 m_panPointerPosition;
DirectX::XMFLOAT3 m_panCommand;
internal:
// Accessor to set the position of the controller
void SetPosition( _In_ DirectX::XMFLOAT3 pos );
// Accessor to set the fixed "look point" of the controller
DirectX::XMFLOAT3 get_FixedLookPoint();
// Returns the position of the controller object
DirectX::XMFLOAT3 get_Position();
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
);
// Set up the Controls supported by this controller
void Initialize( _In_ Windows::UI::Core::CoreWindow^ window );
void Update( Windows::UI::Core::CoreWindow ^window );
}; // Class CameraPanController
Die privaten Felder enthalten den aktuellen Zustand des Kameracontrollers. Lassen Sie uns diese überprüfen.
- m_position ist die Position der Kamera im Szenenbereich. In diesem Beispiel wird der Z-Koordinatenwert auf 0 festgelegt. Wir könnten einen DirectX::XMFLOAT2 verwenden, um diesen Wert darzustellen, aber für die Zwecke dieses Beispiels und der zukünftigen Erweiterbarkeit verwenden wir ein DirectX::XMFLOAT3. Wir übergeben diesen Wert über die get_Position-Eigenschaft an die App selbst, damit der Viewport entsprechend aktualisiert werden kann.
- m_panInUse ist ein boolescher Wert, der angibt, ob ein Schwenkvorgang aktiv ist, oder genauer gesagt, ob der Spieler den Bildschirm berührt und die Kamera bewegt.
- m_panPointerID ist eine eindeutige ID für den Zeiger. Dies wird im Beispiel nicht verwendet, es empfiehlt sich jedoch, die Controllerzustandsklasse einem bestimmten Zeiger zuzuordnen.
- m_panFirstDown ist der Punkt auf dem Bildschirm, an dem der Spieler den Bildschirm zuerst berührt oder während der Kameraverschiebungsaktion mit der Maus geklickt hat. Wir verwenden diesen Wert später, um einen inaktiven Bereich festzulegen, um jittern zu verhindern, wenn der Bildschirm berührt wird, oder wenn die Maus ein wenig schüttelt.
- m_panPointerPosition ist der Punkt auf dem Bildschirm, an dem der Spieler den Zeiger aktuell bewegt hat. Wir verwenden es, um zu bestimmen, welche Richtung der Spieler bewegen wollte, indem wir ihn relativ zu m_panFirstDown untersuchen.
- m_panCommand ist der letzte berechnete Befehl für den Kameracontroller: nach oben, unten, links oder rechts. Da wir mit einer Kamera arbeiten, die auf die X-Y-Ebene festgelegt ist, kann dies stattdessen ein DirectX::XMFLOAT2-Wert sein.
Wir verwenden diese 3 Ereignishandler, um die Zustandsinformationen des Kameracontrollers zu aktualisieren.
- OnPointerPressed ist ein Ereignishandler, den unsere App aufruft, wenn die Spieler einen Finger auf die Touchoberfläche drücken und der Zeiger an die Koordinaten des Drückens verschoben wird.
- OnPointerMoved ist ein Ereignishandler, den unsere App aufruft, wenn der Spieler einen Finger über die Touchoberfläche streift. Es wird mit den neuen Koordinaten des Ziehpfads aktualisiert.
- OnPointerReleased ist ein Ereignishandler, den unsere App aufruft, wenn der Spieler den Finger von der Touchoberfläche entfernt.
Schließlich verwenden wir diese Methoden und Eigenschaften zum Initialisieren, Zugreifen und Aktualisieren der Zustandsinformationen des Kameracontrollers.
- Initialisieren ist ein Ereignishandler, den unsere App aufruft, um die Steuerelemente zu initialisieren und an das CoreWindow-Objekt anzufügen, das Das Anzeigefenster beschreibt.
- SetPosition ist eine Methode, die von unserer App aufgerufen wird, um die Koordinaten (x, y und z) ihrer Steuerelemente im Szenenbereich festzulegen. Beachten Sie, dass unsere Z-Koordinate in diesem Lernprogramm 0 ist.
- get_Position ist eine Eigenschaft, auf die unsere App zugreift, um die aktuelle Position der Kamera im Szenenbereich abzurufen. Sie verwenden diese Eigenschaft als Kommunikation der aktuellen Kameraposition an die App.
- get_FixedLookPoint ist eine Eigenschaft, auf die unsere App zugreift, um den aktuellen Punkt abzurufen, an den die Controllerkamera gerichtet ist. In diesem Beispiel ist sie für die X-Y-Ebene normal gesperrt.
- Update ist eine Methode, die den Controllerzustand liest und die Kameraposition aktualisiert. Sie rufen dieses <Element> 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 zum Implementieren von Touchsteuerungen benötigen. Sie können erkennen, wann und wo die Touch- oder Mauszeigerereignisse aufgetreten sind und was die Aktion ist. Sie können die Position und Ausrichtung der Kamera relativ zum Szenenbereich festlegen und die Änderungen nachverfolgen. Schließlich können Sie die neue Kameraposition mit der aufrufenden App kommunizieren.
Jetzt verbinden wir diese Teile miteinander.
Erstellen der grundlegenden Touchereignisse
Der Windows-Runtime-Ereignis dispatcher stellt drei Ereignisse bereit, die von unserer App behandelt werden 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. Weitere Informationen finden Sie unter Einrichten Ihrer UWP-C++-App zum Anzeigen einer DirectX-Ansicht.
Wenn diese Ereignisse ausgelöst werden, während unsere App ausgeführt wird, aktualisieren die Handler die in unseren privaten Feldern definierten Zustandsinformationen des Kameracontrollers.
Als Erstes füllen wir die Ereignishandler des Touchpointers 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 den Bildschirm berührt oder mit der Maus klickt.
OnPointerPressed
void CameraPanController::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 ( !m_panInUse ) // If no pointer is in this control yet.
{
m_panFirstDown = position; // Save the location of the initial contact.
m_panPointerPosition = position;
m_panPointerID = pointerID; // Store the id of the pointer using this control.
m_panInUse = TRUE;
}
}
Wir verwenden diesen Handler, um die aktuelle CameraPanController-Instanz darüber zu informieren, dass der Kameracontroller als aktiv behandelt werden soll, indem m_panInUse auf TRUE festgelegt wird. Auf diese Weise verwendet die App beim Aufrufen von Update die aktuellen Positionsdaten, um den Viewport zu aktualisieren.
Da wir nun die Basiswerte für die Kamerabewegung eingerichtet haben, wenn der Benutzer den Bildschirm berührt oder im Anzeigefenster klickt, müssen wir bestimmen, was zu tun ist, wenn der Benutzer entweder den Bildschirm drückt oder die Maus mit gedrückter Taste bewegt.
Der OnPointerMoved-Ereignishandler wird immer ausgelöst, wenn der Zeiger bewegt wird, bei jedem Teilstrich, den der Spieler auf dem Bildschirm zieht. Wir müssen die App über die aktuelle Position des Zeigers informiert halten, und dies ist, wie wir dies tun.
OnPointerMoved
void CameraPanController::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 );
m_panPointerPosition = position;
}
Schließlich müssen wir das Verhalten der Kameraverschiebung deaktivieren, wenn der Spieler nicht mehr den Bildschirm berührt. Wir verwenden OnPointerReleased, der aufgerufen wird, wenn PointerReleased ausgelöst wird, um m_panInUse auf FALSE festzulegen und die Kameraverschiebungsbewegung zu deaktivieren und die Zeiger-ID auf 0 festzulegen.
OnPointerReleased
void CameraPanController::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 );
m_panInUse = FALSE;
m_panPointerID = 0;
}
Initialisieren der Touchsteuerelemente und des Controllerzustands
Lassen Sie uns die Ereignisse verbinden und alle grundlegenden Zustandsfelder des Kameracontrollers initialisieren.
Initialize
void CameraPanController::Initialize( _In_ CoreWindow^ window )
{
// Start receiving touch/mouse events.
window->PointerPressed +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerPressed);
window->PointerMoved +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerMoved);
window->PointerReleased +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerReleased);
// Initialize the state of the controller.
m_panInUse = FALSE;
m_panPointerID = 0;
// Initialize this as it is reset on every frame.
m_panCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );
}
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.
Abrufen und Festlegen der Position des Kameracontrollers
Definieren wir einige Methoden zum Abrufen und Festlegen der Position des Kameracontrollers im Szenenbereich.
void CameraPanController::SetPosition( _In_ DirectX::XMFLOAT3 pos )
{
m_position = pos;
}
// Returns the position of the controller object
DirectX::XMFLOAT3 CameraPanController::get_Position()
{
return m_position;
}
DirectX::XMFLOAT3 CameraPanController::get_FixedLookPoint()
{
// For this sample, we don't need to use the trig functions because our
// look point is fixed.
DirectX::XMFLOAT3 result= m_position;
result.z += 1.0f;
return result;
}
SetPosition ist eine öffentliche Methode, die wir von unserer App aufrufen können, wenn wir die Position des Kameracontrollers auf einen bestimmten Punkt festlegen müssen.
get_Position ist unsere wichtigste öffentliche Eigenschaft: So erhält unsere App die aktuelle Position des Kameracontrollers im Szenenbereich, damit er den Viewport entsprechend aktualisieren kann.
get_FixedLookPoint ist eine öffentliche Eigenschaft, die in diesem Beispiel einen Blickpunkt abruft, der für die X-Y-Ebene normal ist. Sie können diese Methode ändern, um die trigonometrischen Funktionen, Sin und Cos zu verwenden, wenn Sie bei der Berechnung der x-, y- und z-Koordinatenwerte mehr schräge Winkel für die feste Kamera erstellen möchten.
Aktualisieren der Zustandsinformationen des Kameracontrollers
Jetzt führen wir unsere Berechnungen durch, mit denen die in m_panPointerPosition nachverfolgten Zeigerkoordinateninformationen in neue Koordinateninformationen für unseren 3D-Szenenbereich konvertiert werden. Unsere App ruft diese Methode jedes Mal auf, wenn die Haupt-App-Schleife aktualisiert wird. Darin berechnen wir die neuen Positionsinformationen, die wir an die App übergeben möchten, mit der die Ansichtsmatrix vor der Projektion in den Viewport aktualisiert wird.
void CameraPanController::Update( CoreWindow ^window )
{
if ( m_panInUse )
{
pointerDelta.x = m_panPointerPosition.x - m_panFirstDown.x;
pointerDelta.y = m_panPointerPosition.y - m_panFirstDown.y;
if ( pointerDelta.x > 16.0f ) // Leave 32 pixel-wide dead spot for being still.
m_panCommand.x += 1.0f;
else
if ( pointerDelta.x < -16.0f )
m_panCommand.x += -1.0f;
if ( pointerDelta.y > 16.0f )
m_panCommand.y += 1.0f;
else
if (pointerDelta.y < -16.0f )
m_panCommand.y += -1.0f;
}
DirectX::XMFLOAT3 command = m_panCommand;
// Our velocity is based on the command.
DirectX::XMFLOAT3 Velocity;
Velocity.x = command.x;
Velocity.y = command.y;
Velocity.z = 0.0f;
// Integrate
m_position.x = m_position.x + Velocity.x;
m_position.y = m_position.y + Velocity.y;
m_position.z = m_position.z + Velocity.z;
// Clear the movement input accumulator for use during the next frame.
m_panCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );
}
Da touch- oder mouse jitter nicht dazu führen soll, dass unsere Kamera ruckelt, legen wir eine inaktive Zone um den Zeiger mit einem Durchmesser von 32 Pixeln fest. Wir haben auch einen Geschwindigkeitswert, der in diesem Fall 1:1 mit dem Pixel-Traversal des Zeigers über die inaktive Zone liegt. Sie können dieses Verhalten anpassen, um die Bewegungsrate zu verlangsamen oder zu beschleunigen.
Aktualisieren der Ansichtsmatrix mit der neuen Kameraposition
Wir können jetzt eine Szenenbereichkoordinate abrufen, auf die sich unsere Kamera konzentriert und aktualisiert wird, wenn Sie Ihre App dazu aufweisen (z. B. alle 60 Sekunden in der Haupt-App-Schleife). Dieser Pseudocode schlägt das Aufrufverhalten vor, das Sie implementieren können:
myCameraPanController->Update( m_window );
// Update the view matrix based on the camera position.
myCamera->MyMethodToComputeViewMatrix(
myController->get_Position(), // The position in the 3D scene space.
myController->get_FixedLookPoint(), // The point in the space we are looking at.
DirectX::XMFLOAT3( 0, 1, 0 ) // The axis that is "up" in our space.
);
Herzlichen Glückwunsch! Sie haben einen einfachen Satz von Kameraverschiebungs-Touchsteuerungen in Ihrem Spiel implementiert.