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.
Spiele der universellen Windows-Plattform (UWP) werden auf einer Vielzahl von Geräten ausgeführt, z. B. Desktopcomputer, Laptops und Tablets. Ein Gerät kann über eine Vielzahl von Eingabe- und Steuerungsmechanismen verfügen. In diesem Dokument werden die wichtigsten Methoden beschrieben, die Sie berücksichtigen sollten, wenn Sie mit Eingabegeräten arbeiten, und zeigt, wie Marble Maze diese Methoden anwendet.
Hinweis
Der Beispielcode, der diesem Dokument entspricht, befindet sich im DirectX Marble Maze-Spielbeispiel.
Hier sind einige der wichtigsten Punkte, die in diesem Dokument erläutert werden, wenn Sie mit Eingaben in Ihrem Spiel arbeiten:
Unterstützen Sie nach Möglichkeit mehrere Eingabegeräte, damit Ihr Spiel eine größere Auswahl an Vorlieben und Fähigkeiten für Ihre Kunden bietet. Obwohl die Verwendung von Gamecontrollern und Sensoren optional ist, empfehlen wir sie nachdrücklich, um das Spielerlebnis zu verbessern. Wir haben die Gamecontroller- und Sensor-APIs entwickelt, damit Sie diese Eingabegeräte einfacher integrieren können.
Zum Initialisieren der Toucheingabe müssen Sie sich für Fensterereignisse registrieren, z. B. wenn der Zeiger aktiviert, losgelassen und verschoben wird. Um den Beschleunigungsmesser zu initialisieren, erstellen Sie beim Starten der Anwendung ein Windows::Devices::Sensors::Accelerometer Objekt. Ein Gamecontroller erfordert keine Initialisierung.
Überlegen Sie bei Spielen mit einem Spieler, ob Sie Eingaben von allen möglichen Controllern kombinieren möchten. Auf diese Weise müssen Sie nicht nachverfolgen, welche Eingaben von welchem Controller stammen. Oder verfolgen Sie einfach die Eingaben nur vom zuletzt hinzugefügten Controller; wie wir es in diesem Beispiel tun.
Verarbeiten sie Windows-Ereignisse, bevor Sie Eingabegeräte verarbeiten.
Game-Controller und Beschleunigungsmesser unterstützen die Abfrage. Das heißt, Sie können daten abfragen, wenn Sie sie benötigen. Für Berührungen zeichnen Sie Berührungsereignisse in Datenstrukturen auf, die für Ihren Eingabeverarbeitungscode verfügbar sind.
Überlegen Sie, ob Eingabewerte in ein gängiges Format normalisiert werden sollen. Auf diese Weise können Sie vereinfachen, wie eingaben von anderen Komponenten Ihres Spiels interpretiert werden, z. B. physiksimulation, und sie können das Schreiben von Spielen vereinfachen, die auf unterschiedlichen Bildschirmauflösungen funktionieren.
Von Marble Maze unterstützte Eingabegeräte
Marble Maze unterstützt den Gamecontroller, die Maus und die Toucheingabe zum Auswählen von Menüelementen sowie den Gamecontroller, die Maus, die Toucheingabe und den Beschleunigungsmesser zum Steuern des Spielspiels. Marble Maze verwendet die Windows::Gaming::Input-APIs, um den Controller zur Eingabe abzufragen. Mit der Toucheingabe können Anwendungen Fingerspitzeneingaben nachverfolgen und darauf reagieren. Ein Beschleunigungsmesser ist ein Sensor, der die Kraft misst, die entlang der X-, Y- und Z-Achsen angewendet wird. Mithilfe der Windows-Runtime können Sie den aktuellen Zustand des Beschleunigungsmessergeräts abrufen sowie Touchereignisse über den Ereignisbehandlungsmechanismus der Windows-Runtime empfangen.
Hinweis
In diesem Dokument wird der Begriff "Touch" verwendet, um sowohl auf Toucheingabe als auch auf Mauseingaben zu verweisen, und "Zeiger", um alle Geräte zu bezeichnen, die Zeigerereignisse verwenden. Da Berührungsbildschirm und Maus Standard-Zeigerereignisse verwenden, können Sie beide Geräte nutzen, um Menüelemente auszuwählen und das Spielen zu steuern.
Hinweis
Das Paketmanifest legt Querformat als die einzige unterstützte Ausrichtung für das Spiel fest, um zu verhindern, dass die Ausrichtung sich ändert, wenn Sie das Gerät drehen, um die Murmel zu rollen. Um das Paketmanifest anzuzeigen, öffnen Sie Package.appxmanifest- im Projektmappen-Explorer in Visual Studio.
Initialisieren von Eingabegeräten
Der Gamecontroller erfordert keine Initialisierung. Um die Toucheingabe zu initialisieren, müssen Sie sich für Fensterereignisse registrieren. Dazu gehört, dass der Zeiger aktiviert, losgelassen und verschoben wird (z. B. wenn der Spieler die Maustaste drückt oder den Bildschirm berührt). Zum Initialisieren des Beschleunigungsmessers müssen Sie beim Initialisieren der Anwendung ein Windows::Devices::Sensors::Accelerometer-Objekt erstellen.
Das folgende Beispiel zeigt, wie die Methode App::SetWindow für die Registrierung der Zeigerereignisse Windows::UI::Core::CoreWindow::PointerPressed, Windows::UI::Core::CoreWindow::PointerReleasedund Windows::UI::Core::CoreWindow::PointerMoved verwendet wird. Diese Ereignisse werden während der Anwendungsinitialisierung und vor der Spielschleife registriert.
Diese Ereignisse werden in einem separaten Thread behandelt, der die Ereignishandler aufruft.
Weitere Informationen darüber, wie die Anwendung initialisiert wird, finden Sie unter Marble Maze-Anwendungsstruktur.
window->PointerPressed += ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(
this,
&App::OnPointerPressed);
window->PointerReleased += ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(
this,
&App::OnPointerReleased);
window->PointerMoved += ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(
this,
&App::OnPointerMoved);
Die MarbleMazeMain Klasse erstellt außerdem ein std::map Objekt zum Speichern von Touchereignissen. Der Schlüssel für dieses Kartenobjekt ist ein Wert, der den Eingabezeiger eindeutig identifiziert. Jede Taste ist dem Abstand zwischen allen Berührungspunkten und der Mitte des Bildschirms zugeordnet. Marble Maze verwendet später diese Werte, um den Winkel zu berechnen, um den das Labyrinth gekippt wird.
typedef std::map<int, XMFLOAT2> TouchMap;
TouchMap m_touches;
Die MarbleMazeMain Klasse enthält auch ein Beschleunigungsmesser--Objekt.
Windows::Devices::Sensors::Accelerometer^ m_accelerometer;
Das Beschleunigungsmesser--Objekt wird im MarbleMazeMain-Konstruktor initialisiert, wie im folgenden Beispiel gezeigt. Die Windows::Devices::Sensors::Accelerometer::GetDefault-Methode gibt eine Instanz des Standardbeschleunigungsmessers zurück. Wenn kein Standardbeschleunigungsmesser vorhanden ist, gibt Beschleunigungsmesser::GetDefaultnullptrzurück.
// Returns accelerometer ref if there is one; nullptr otherwise.
m_accelerometer = Windows::Devices::Sensors::Accelerometer::GetDefault();
Navigieren in den Menüs
Sie können die Maus, Toucheingabe oder einen Gamecontroller verwenden, um in den Menüs wie folgt zu navigieren:
- Verwenden Sie das Steuerkreuz, um das aktive Menüelement zu ändern.
- Verwenden Sie die Toucheingabe, die A-Schaltfläche oder die Menüschaltfläche, um ein Menüelement zu wählen oder das aktuelle Menü zu schließen, z. B. die Highscore-Tabelle.
- Verwenden Sie die Menüschaltfläche, um das Spiel anzuhalten oder fortzusetzen.
- Klicken Sie mit der Maus auf ein Menüelement, um diese Aktion auszuwählen.
Nachverfolgen der Eingabe des Gamecontrollers
Um die derzeit mit dem Gerät verbundenen Gamepads nachzuverfolgen, definiert MarbleMazeMain eine Membervariable, m_myGamepads, die eine Sammlung von Windows::Gaming::Input::Gamepad-Objekten ist. Dies wird im Konstruktor wie folgt initialisiert:
m_myGamepads = ref new Vector<Gamepad^>();
for (auto gamepad : Gamepad::Gamepads)
{
m_myGamepads->Append(gamepad);
}
Darüber hinaus registriert der MarbleMazeMain-Konstruktor Ereignisse, wenn Gamepads hinzugefügt oder entfernt werden.
Gamepad::GamepadAdded +=
ref new EventHandler<Gamepad^>([=](Platform::Object^, Gamepad^ args)
{
m_myGamepads->Append(args);
m_currentGamepadNeedsRefresh = true;
});
Gamepad::GamepadRemoved +=
ref new EventHandler<Gamepad ^>([=](Platform::Object^, Gamepad^ args)
{
unsigned int indexRemoved;
if (m_myGamepads->IndexOf(args, &indexRemoved))
{
m_myGamepads->RemoveAt(indexRemoved);
m_currentGamepadNeedsRefresh = true;
}
});
Wenn ein Gamepad hinzugefügt wird, wird es zu m_myGamepads hinzugefügt. Wenn ein Gamepad entfernt wird, überprüfen wir, ob sich das Gamepad in m_myGamepads befindet, und falls ja, entfernen wir es. In beiden Fällen legen wir m_currentGamepadNeedsRefresh auf truefest, was bedeutet, dass wir m_gamepadneu zuweisen müssen.
Schließlich weisen wir m_gamepad ein Gamepad zu und legen m_currentGamepadNeedsRefresh auf falsefest:
m_gamepad = GetLastGamepad();
m_currentGamepadNeedsRefresh = false;
In der Update-Methode überprüfen wir, ob m_gamepad neu zugewiesen werden muss:
if (m_currentGamepadNeedsRefresh)
{
auto mostRecentGamepad = GetLastGamepad();
if (m_gamepad != mostRecentGamepad)
{
m_gamepad = mostRecentGamepad;
}
m_currentGamepadNeedsRefresh = false;
}
Wenn m_gamepad neu zugewiesen werden muss, weisen wir ihm das zuletzt hinzugefügte Gamepad zu, indem wir GetLastGamepadverwenden, das wie folgt definiert ist:
Gamepad^ MarbleMaze::MarbleMazeMain::GetLastGamepad()
{
Gamepad^ gamepad = nullptr;
if (m_myGamepads->Size > 0)
{
gamepad = m_myGamepads->GetAt(m_myGamepads->Size - 1);
}
return gamepad;
}
Diese Methode gibt einfach das letzte Gamepad in m_myGamepadszurück.
Sie können bis zu vier Gamecontroller mit einem Windows 10-Gerät verbinden. Um zu vermeiden, dass wir herausfinden müssen, welcher Controller der aktive ist, erfassen wir einfach nur das zuletzt hinzugefügte Gamepad. Wenn Ihr Spiel mehrere Spieler unterstützt, müssen Sie die Eingaben für jeden Spieler separat nachverfolgen.
Die MarbleMazeMain::Update-Methode fragt das Gamepad nach Eingaben ab.
if (m_gamepad != nullptr)
{
m_oldReading = m_newReading;
m_newReading = m_gamepad->GetCurrentReading();
}
Wir verfolgen den Eingabewert, den wir im letzten Frame mit m_oldReadingerhalten haben, und den neuesten Eingabewert mit m_newReading, den wir erhalten, indem wir Gamepad::GetCurrentReadingaufrufen. Dadurch wird ein GamepadReading-Objekt zurückgegeben, das Informationen zum aktuellen Zustand des Gamepads enthält.
Um zu überprüfen, ob eine Schaltfläche gerade gedrückt oder losgelassen wurde, definieren wir MarbleMazeMain::ButtonJustPressed und MarbleMazeMain::ButtonJustReleased, die Schaltflächenwerte aus diesem Frame und dem letzten Frame vergleichen. Auf diese Weise können wir eine Aktion nur zu dem Zeitpunkt ausführen, zu dem eine Schaltfläche anfänglich gedrückt oder losgelassen wird, und nicht, wenn sie gehalten wird:
bool MarbleMaze::MarbleMazeMain::ButtonJustPressed(GamepadButtons selection)
{
bool newSelectionPressed = (selection == (m_newReading.Buttons & selection));
bool oldSelectionPressed = (selection == (m_oldReading.Buttons & selection));
return newSelectionPressed && !oldSelectionPressed;
}
bool MarbleMaze::MarbleMazeMain::ButtonJustReleased(GamepadButtons selection)
{
bool newSelectionReleased =
(GamepadButtons::None == (m_newReading.Buttons & selection));
bool oldSelectionReleased =
(GamepadButtons::None == (m_oldReading.Buttons & selection));
return newSelectionReleased && !oldSelectionReleased;
}
GamepadButtons-Lesevorgänge werden mit bitweisen Vorgängen verglichen – wir überprüfen, ob eine Taste mit Bitweise und (&) gedrückt wird. Wir bestimmen, ob eine Taste gerade gedrückt oder losgelassen wurde, indem wir den alten und den neuen Lesewert vergleichen.
Mithilfe der oben genannten Methoden überprüfen wir, ob bestimmte Schaltflächen gedrückt wurden, und führen entsprechende Aktionen aus, die ausgeführt werden müssen. Wenn beispielsweise die Menüschaltfläche (GamepadButtons::Menu) gedrückt wird, ändert sich der Spielzustand von aktiv in angehalten oder angehalten in aktiv.
if (ButtonJustPressed(GamepadButtons::Menu) || m_pauseKeyPressed)
{
m_pauseKeyPressed = false;
if (m_gameState == GameState::InGameActive)
{
SetGameState(GameState::InGamePaused);
}
else if (m_gameState == GameState::InGamePaused)
{
SetGameState(GameState::InGameActive);
}
}
Außerdem überprüfen wir, ob der Spieler die Schaltfläche "Ansicht" drückt. In diesem Fall starten wir das Spiel neu oder löschen die Highscore-Tabelle:
if (ButtonJustPressed(GamepadButtons::View) || m_homeKeyPressed)
{
m_homeKeyPressed = false;
if (m_gameState == GameState::InGameActive ||
m_gameState == GameState::InGamePaused ||
m_gameState == GameState::PreGameCountdown)
{
SetGameState(GameState::MainMenu);
m_inGameStopwatchTimer.SetVisible(false);
m_preGameCountdownTimer.SetVisible(false);
}
else if (m_gameState == GameState::HighScoreDisplay)
{
m_highScoreTable.Reset();
}
}
Wenn das Hauptmenü aktiv ist, ändert sich das aktive Menüelement, wenn das Steuerkreuz nach oben oder unten gedrückt wird. Wenn der Benutzer die aktuelle Auswahl auswählt, wird das entsprechende UI-Element als ausgewählt markiert.
// Handle menu navigation.
bool chooseSelection =
(ButtonJustPressed(GamepadButtons::A)
|| ButtonJustPressed(GamepadButtons::Menu));
bool moveUp = ButtonJustPressed(GamepadButtons::DPadUp);
bool moveDown = ButtonJustPressed(GamepadButtons::DPadDown);
switch (m_gameState)
{
case GameState::MainMenu:
if (chooseSelection)
{
m_audio.PlaySoundEffect(MenuSelectedEvent);
if (m_startGameButton.GetSelected())
{
m_startGameButton.SetPressed(true);
}
if (m_highScoreButton.GetSelected())
{
m_highScoreButton.SetPressed(true);
}
}
if (moveUp || moveDown)
{
m_startGameButton.SetSelected(!m_startGameButton.GetSelected());
m_highScoreButton.SetSelected(!m_startGameButton.GetSelected());
m_audio.PlaySoundEffect(MenuChangeEvent);
}
break;
case GameState::HighScoreDisplay:
if (chooseSelection || anyPoints)
{
SetGameState(GameState::MainMenu);
}
break;
case GameState::PostGameResults:
if (chooseSelection || anyPoints)
{
SetGameState(GameState::HighScoreDisplay);
}
break;
case GameState::InGamePaused:
if (m_pausedText.IsPressed())
{
m_pausedText.SetPressed(false);
SetGameState(GameState::InGameActive);
}
break;
}
Nachverfolgen von Touch- und Mauseingaben
Bei der Touch- und Mauseingabe wird ein Menüelement ausgewählt, wenn der Benutzer darauf klickt oder berührt. Das folgende Beispiel zeigt, wie die MarbleMazeMain::Update-Methode Zeigereingaben verarbeitet, um Menüelemente auszuwählen. Die m_pointQueue Membervariable verfolgt die Stellen, an denen der Benutzer den Bildschirm berührt oder angeklickt hat. Die Art und Weise, in der Marble Maze Zeigereingaben sammelt, wird weiter unten in diesem Dokument im Abschnitt "Zeigereingabe verarbeiten" ausführlicher beschrieben.
// Check whether the user chose a button from the UI.
bool anyPoints = !m_pointQueue.empty();
while (!m_pointQueue.empty())
{
UserInterface::GetInstance().HitTest(m_pointQueue.front());
m_pointQueue.pop();
}
Die UserInterface::HitTest-Methode ermittelt, ob der angegebene Punkt innerhalb der Grenzen eines Benutzerschnittstellen-Elements liegt. Alle UI-Elemente, die diesen Test bestehen, werden als berührt markiert. Diese Methode verwendet die PointInRect-Hilfsfunktion , um zu bestimmen, ob sich der bereitgestellte Punkt in den Grenzen der einzelnen UI-Elemente befindet.
void UserInterface::HitTest(D2D1_POINT_2F point)
{
for (auto iter = m_elements.begin(); iter != m_elements.end(); ++iter)
{
if (!(*iter)->IsVisible())
continue;
TextButton* textButton = dynamic_cast<TextButton*>(*iter);
if (textButton != nullptr)
{
D2D1_RECT_F bounds = (*iter)->GetBounds();
textButton->SetPressed(PointInRect(point, bounds));
}
}
}
Aktualisieren des Spielzustands
Nachdem die Methode MarbleMazeMain::Update Controller- und Toucheingaben verarbeitet hat, wird der Spielzustand aktualisiert, sofern eine Taste gedrückt wurde.
// Update the game state if the user chose a menu option.
if (m_startGameButton.IsPressed())
{
SetGameState(GameState::PreGameCountdown);
m_startGameButton.SetPressed(false);
}
if (m_highScoreButton.IsPressed())
{
SetGameState(GameState::HighScoreDisplay);
m_highScoreButton.SetPressed(false);
}
Spielsteuerung
Die Spielschleife und die MarbleMazeMain::Update-Methode arbeiten zusammen, um den Zustand von Spielobjekten zu aktualisieren. Wenn Ihr Spiel Eingaben von mehreren Geräten akzeptiert, können Sie die Eingaben von allen Geräten in einer Gruppe von Variablen sammeln, sodass Sie Code schreiben können, der einfacher zu verwalten ist. Die MarbleMazeMain::Update-Methode definiert einen Satz von Variablen, die Bewegungen von allen Geräten ansammeln.
float combinedTiltX = 0.0f;
float combinedTiltY = 0.0f;
Der Eingabemechanismus kann von einem Eingabegerät zu einem anderen variieren. Zeigereingaben werden z. B. mithilfe des Windows-Runtime-Ereignisbehandlungsmodells behandelt. Umgekehrt rufen Sie bei Bedarf Eingabedaten vom Gamecontroller ab. Es wird empfohlen, immer dem Eingabemechanismus zu folgen, der für ein bestimmtes Gerät vorgeschrieben ist. In diesem Abschnitt wird beschrieben, wie Marble Maze Eingaben von jedem Gerät liest, wie die kombinierten Eingabewerte aktualisiert werden und wie die kombinierten Eingabewerte verwendet werden, um den Zustand des Spiels zu aktualisieren.
Verarbeiten von Zeigereingaben
Wenn Sie mit Zeigereingaben arbeiten, rufen Sie die Windows::UI::Core::CoreDispatcher::ProcessEvents-Methode auf, um Fensterereignisse zu verarbeiten. Rufen Sie diese Methode in der Spielschleife auf, bevor Sie die Szene rendern oder aktualisieren. In der App::Run-Methode ruft Marble Maze dies auf:
while (!m_windowClosed)
{
if (m_windowVisible)
{
CoreWindow::GetForCurrentThread()->
Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
m_main->Update();
if (m_main->Render())
{
m_deviceResources->Present();
}
}
else
{
CoreWindow::GetForCurrentThread()->
Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
}
}
Wenn das Fenster sichtbar ist, übergeben wir CoreProcessEventsOption::ProcessAllIfPresent an ProcessEvents, um alle Ereignisse in der Warteschlange zu verarbeiten und sofort zurückzukehren; andernfalls übergeben wir CoreProcessEventsOption::ProcessOneAndAllPending, um alle Ereignisse in der Warteschlange zu verarbeiten und auf das nächste neue Ereignis zu warten. Nachdem Ereignisse verarbeitet wurden, rendert Marble Maze den nächsten Frame und zeigt ihn an.
Die Windows-Runtime ruft den registrierten Handler für jedes aufgetretene Ereignis auf. Die App::SetWindow--Methode registriert sich für Ereignisse und leitet Zeigerinformationen an die MarbleMazeMain Klasse weiter.
void App::OnPointerPressed(
Windows::UI::Core::CoreWindow^ sender,
Windows::UI::Core::PointerEventArgs^ args)
{
m_main->AddTouch(args->CurrentPoint->PointerId, args->CurrentPoint->Position);
}
void App::OnPointerReleased(
Windows::UI::Core::CoreWindow^ sender,
Windows::UI::Core::PointerEventArgs^ args)
{
m_main->RemoveTouch(args->CurrentPoint->PointerId);
}
void App::OnPointerMoved(
Windows::UI::Core::CoreWindow^ sender,
Windows::UI::Core::PointerEventArgs^ args)
{
m_main->UpdateTouch(args->CurrentPoint->PointerId, args->CurrentPoint->Position);
}
Die MarbleMazeMain Klasse reagiert auf Zeigerereignisse, indem das Kartenobjekt aktualisiert wird, das Touchereignisse enthält. Die MarbleMazeMain::AddTouch-Methode wird aufgerufen, wenn der Zeiger zum ersten Mal gedrückt wird, z. B. wenn der Benutzer den Bildschirm anfänglich auf einem gerät mit Toucheingabe berührt. Die MarbleMazeMain::UpdateTouch-Methode wird aufgerufen, wenn die Zeigerposition verschoben wird. Die MarbleMazeMain::RemoveTouch-Methode wird aufgerufen, wenn der Zeiger losgelassen wird, z. B. wenn der Benutzer das Berühren des Bildschirms beendet.
void MarbleMazeMain::AddTouch(int id, Windows::Foundation::Point point)
{
m_touches[id] = PointToTouch(point, m_deviceResources->GetLogicalSize());
m_pointQueue.push(D2D1::Point2F(point.X, point.Y));
}
void MarbleMazeMain::UpdateTouch(int id, Windows::Foundation::Point point)
{
if (m_touches.find(id) != m_touches.end())
m_touches[id] = PointToTouch(point, m_deviceResources->GetLogicalSize());
}
void MarbleMazeMain::RemoveTouch(int id)
{
m_touches.erase(id);
}
Die PointToTouch-Funktion übersetzt die aktuelle Zeigerposition so, dass sich der Ursprung in der Mitte des Bildschirms befindet, und skaliert dann die Koordinaten so, dass sie ungefähr zwischen -1,0 und +1,0 liegen. Dadurch ist es einfacher, die Neigung des Labyrinths auf einheitliche Weise über verschiedene Eingabemethoden hinweg zu berechnen.
inline XMFLOAT2 PointToTouch(Windows::Foundation::Point point, Windows::Foundation::Size bounds)
{
float touchRadius = min(bounds.Width, bounds.Height);
float dx = (point.X - (bounds.Width / 2.0f)) / touchRadius;
float dy = ((bounds.Height / 2.0f) - point.Y) / touchRadius;
return XMFLOAT2(dx, dy);
}
Die MarbleMazeMain::Update-Methode aktualisiert die kombinierten Eingabewerte, indem sie den Neigungsfaktor um einen konstanten Skalierungswert vergrößert. Dieser Skalierungswert wurde durch Experimentieren mit mehreren verschiedenen Werten bestimmt.
// Account for touch input.
for (TouchMap::const_iterator iter = m_touches.cbegin();
iter != m_touches.cend();
++iter)
{
combinedTiltX += iter->second.x * m_touchScaleFactor;
combinedTiltY += iter->second.y * m_touchScaleFactor;
}
Verarbeitung von Eingabedaten des Beschleunigungssensors
Zum Verarbeiten der Beschleunigungsmesserwerte ruft die MarbleMazeMain::Update-Methode die Windows::Devices::Sensors::Accelerometer::GetCurrentReading-Methode auf. Diese Methode gibt ein Windows::Devices::Sensors::AccelerometerReading-Objekt zurück, das einen Beschleunigungsmesserwert darstellt. Die Windows::Devices::Sensors::AccelerometerReading::AccelerationX und Windows::Devices::Sensors::AccelerometerReading::AccelerationY Eigenschaften enthalten die g-Kraft-Beschleunigung entlang der X- bzw. Y-Achse.
Das folgende Beispiel zeigt, wie die MarbleMazeMain::Update - Methode den Beschleunigungsmesser abruft und die kombinierten Eingabewerte aktualisiert. Wenn Sie das Gerät kippen, bewirkt die Schwerkraft, dass sich die Murmel schneller bewegt.
// Account for sensors.
if (m_accelerometer != nullptr)
{
Windows::Devices::Sensors::AccelerometerReading^ reading =
m_accelerometer->GetCurrentReading();
if (reading != nullptr)
{
combinedTiltX +=
static_cast<float>(reading->AccelerationX) * m_accelerometerScaleFactor;
combinedTiltY +=
static_cast<float>(reading->AccelerationY) * m_accelerometerScaleFactor;
}
}
Da Sie nicht sicher sein können, dass ein Beschleunigungsmesser auf dem Computer des Benutzers vorhanden ist, stellen Sie immer sicher, dass Sie über ein gültiges Beschleunigungsmesser Objekt verfügen, bevor Sie den Beschleunigungsmesser abfragen.
Verarbeiten von Gamecontrollereingaben
In der MarbleMazeMain::Update-Methode verwenden wir m_newReading , um Eingaben vom linken Analogstick zu verarbeiten:
float leftStickX = static_cast<float>(m_newReading.LeftThumbstickX);
float leftStickY = static_cast<float>(m_newReading.LeftThumbstickY);
auto oppositeSquared = leftStickY * leftStickY;
auto adjacentSquared = leftStickX * leftStickX;
if ((oppositeSquared + adjacentSquared) > m_deadzoneSquared)
{
combinedTiltX += leftStickX * m_controllerScaleFactor;
combinedTiltY += leftStickY * m_controllerScaleFactor;
}
Wir prüfen, ob sich die Eingabe des linken Analogsticks außerhalb der Totzone befindet. Wenn dies der Fall ist, addieren wir sie zu combinedTiltX und combinedTiltY (multipliziert mit einem Skalierungsfaktor), um die Bühne zu kippen.
Von Bedeutung
Wenn Sie mit einem Gamecontroller arbeiten, müssen Sie immer den inaktiven Bereich berücksichtigen. Die Totzone bezieht sich auf die Unterschiede zwischen Gamepads in Bezug auf ihre Empfindlichkeit bei den anfänglichen Bewegungen. In einigen Controllern führt eine kleine Bewegung möglicherweise zu keinem Ergebnis, während sie in anderen einen messbaren Wert ergeben kann. Um dies in Ihrem Spiel zu berücksichtigen, erstellen Sie eine bewegungsfreie Zone für die anfängliche Joystick-Bewegung. Weitere Informationen zur Totzone finden Sie unter Verwendung der Ministicks.
Anwenden von Eingaben auf den Spielzustand
Geräte melden Eingabewerte auf unterschiedliche Weise. Die Zeigereingabe kann sich z. B. in Bildschirmkoordinaten befinden, und die Controllereingabe weist ein völlig anderes Format auf. Eine Herausforderung beim Kombinieren von Eingaben von mehreren Geräten in einer Reihe von Eingabewerten ist die Normalisierung oder das Konvertieren von Werten in ein gängiges Format. Marble Maze normalisiert Werte, indem sie auf den Bereich [-1,0, 1,0] skaliert werden. Die in diesem Abschnitt beschriebene PointToTouch-Funktion konvertiert Bildschirmkoordinaten in normalisierte Werte, die ungefähr zwischen -1,0 und +1,0 liegen.
Tipp
Auch wenn Ihre Anwendung eine Eingabemethode verwendet, empfehlen wir, eingabewerte immer zu normalisieren. Auf diese Weise können Sie vereinfachen, wie eingaben von anderen Komponenten Ihres Spiels interpretiert werden, z. B. physikalische Simulationen, und es erleichtert das Schreiben von Spielen, die auf unterschiedlichen Bildschirmauflösungen funktionieren.
Nachdem die MarbleMazeMain::Update-Methode Eingaben verarbeitet hat, wird ein Vektor erstellt, der die Auswirkung der Neigung des Labyrinths auf die Murmel darstellt. Das folgende Beispiel zeigt, wie Marble Maze die XMVector3Normalize-Funktion verwendet, um einen normalisierten Schwerkraftvektor zu erstellen. Die maxTilt- Variable begrenzt den Neigungswinkel des Labyrinths und verhindert, dass es seitlich kippt.
const float maxTilt = 1.0f / 8.0f;
XMVECTOR gravity = XMVectorSet(
combinedTiltX * maxTilt,
combinedTiltY * maxTilt,
1.0f,
0.0f);
gravity = XMVector3Normalize(gravity);
Um die Aktualisierung von Szenenobjekten abzuschließen, übergibt Marble Maze den aktualisierten Schwerkraftvektor an die Physikalische Simulation, aktualisiert die Physiksimulation für die Zeit, die seit dem vorherigen Frame verstrichen ist, und aktualisiert die Position und Ausrichtung der Murmel. Wenn die Murmel durch das Labyrinth gefallen ist, platziert die MarbleMazeMain::Update-Methode die Murmel am letzten Prüfpunkt, den die Murmel berührt hat, und setzt den Zustand der Physiksimulation zurück.
XMFLOAT3A g;
XMStoreFloat3(&g, gravity);
m_physics.SetGravity(g);
if (m_gameState == GameState::InGameActive)
{
// Only update physics when gameplay is active.
m_physics.UpdatePhysicsSimulation(static_cast<float>(m_timer.GetElapsedSeconds()));
// ...Code omitted for simplicity...
}
// ...Code omitted for simplicity...
// Check whether the marble fell off of the maze.
const float fadeOutDepth = 0.0f;
const float resetDepth = 80.0f;
if (marblePosition.z >= fadeOutDepth)
{
m_targetLightStrength = 0.0f;
}
if (marblePosition.z >= resetDepth)
{
// Reset marble.
memcpy(&marblePosition, &m_checkpoints[m_currentCheckpoint], sizeof(XMFLOAT3));
oldMarblePosition = marblePosition;
m_physics.SetPosition((const XMFLOAT3&)marblePosition);
m_physics.SetVelocity(XMFLOAT3(0, 0, 0));
m_lightStrength = 0.0f;
m_targetLightStrength = 1.0f;
m_resetCamera = true;
m_resetMarbleRotation = true;
m_audio.PlaySoundEffect(FallingEvent);
}
In diesem Abschnitt wird nicht beschrieben, wie die Physiksimulation funktioniert. Ausführliche Informationen hierzu finden Sie unter Physics.h und Physics.cpp in den Marble Maze-Quellen.
Nächste Schritte
Lesen Sie Hinzufügen von Audio zum Marble Maze-Beispiel, um Informationen über einige wichtige Methoden zu erhalten, auf die Sie achten sollten, wenn Sie mit Audio arbeiten. Im Dokument wird erläutert, wie Marble Maze Microsoft Media Foundation und XAudio2 zum Laden, Mischen und Wiedergeben von Audioressourcen verwendet.
Zugehörige Themen
- Hinzufügen von Audio zum Marble Maze-Beispiel
- Hinzufügen visueller Inhalte zum Marble Maze-Beispiel
- Entwickeln von Marble Maze, einem UWP-Spiel in C++ und DirectX