Verwenden von Ereignismeldungen und CoreWindow
Inhaltsverzeichnis reduzieren
Inhaltsverzeichnis erweitern

Verwenden von Ereignismeldungen und CoreWindow (DirectX und C++)

[ Dieser Artikel richtet sich an Windows 8.x- und Windows Phone 8.x-Entwickler, die Windows-Runtime-Apps schreiben. Wenn Sie für Windows 10 entwickeln, finden Sie weitere Informationen unter neueste Dokumentation ]

Dieses Thema behandelt Eingabe- und Zeichnungs-Ereignismeldungen mit dem CoreWindow-Typ in Ihrer Windows-Runtime-App mit DirectX.

Ereignisgesteuerte und timergesteuerte Windows-Runtime-Apps mit DirectX

Bei den meisten Spielen und Echtzeitgrafik-Apps ist ein Gefühl der Kontrolle und Dynamik von höchster Bedeutung. Um dies zu erzielen, muss ein sofort spürbarer Zusammenhang zwischen der Benutzereingabe und dem bereitgestellten Feedback in Form von Ausgabe (in der Regel Grafiken) bestehen. Als Entwickler hochleistungsfähiger Windows-Runtime-Apps mit DirectX haben Sie eine Entscheidung zu treffen: Verknüpfen Sie die Anzeige der aktualisierten Grafiken direkt mit auftretenden Eingabeereignissen, oder zeigen Sie die aktualisierten Grafiken in einem separaten timergesteuerten Intervall an?

Beide Verfahren haben Vorteile und eignen sich jeweils für bestimmte Spiele. Im Großen und Ganzen können Sie Code weniger komplex gestalten, wenn Sie die Swapchain bei allen eingehenden Ereignissen anzeigen. (Die Swapchain enthält die aktuellen vom Spielzustand bestimmten transformierten und projektierten Grafiken sowie Renderobjekte.) Und es muss ein Timer weniger nachverfolgt werden! Wenn sich die einfachen Steuerelemente Ihres Spiels oder Ihrer App weitgehend diskret verhalten und nur einige Fingereingabeereignisse generiert werden (z. B. in einem rundenbasiertes Strategiespiel, einem Schachspiel oder einem Modell-Viewer), ist diese Methode geeignet.

In diesem Fall rufen Sie mithilfe von CoreProcessEventsOption.ProcessUntilQuit das CoreDispatcher::ProcessEvents-Objekt auf, das der Warteschlange weiterhin Eingabeereignismeldungen hinzufügt und diese verteilt, bis das entsprechende CoreWindow-Objekt beendet wird. Von den HTML- und Windows Store-Apps, die XAML-Modelle verwenden, werden Eingabeereignisse auf diese Weise behandelt.

In Bezug auf dem Code wird diesem Muster gefolgt:


void MyApp::Run()
{
	// ...
	m_coreWindow->Dispatcher->ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
	// ...
}

void MyApp::OnInputEvent()
{
	// Process input and update scene accordingly ...
	Render(); // perform the transformation and projection of the scene into a 2D buffer
	Present(); // draw the 2D buffer to the screen by presenting the swap chain that holds it
}

Dieses Modell versetzt auch den UI-Thread automatisch in den Ruhezustand, wenn sich in der Meldungswarteschlange keine Eingabeereignisse befinden.

Für die meisten DirectX-Spiele eignet sich jedoch die Verwendung eines Timers und CoreProcessEventsOption.ProcessAllIfPresent am besten. Mit diesem Verfahren behandelt Ihre App die Eingabeereignisse, die im Intervall des Timers eingehen und zeigt im gleichen Intervall die Swapchain an. Nähere Informationen hierzu erhalten Sie später.

Weitere Informationen zur CoreWindow-Ereignisverteilung finden Sie unter Das App-Objekt und DirectX.

Behandeln von häufigen Eingabeereignissen

Jedes Eingabeereignis – ein Fingereingabezeiger-, ein Mauszeiger- oder ein Tastaturereignis – generiert eine Nachricht, die vom entsprechenden Ereignishandler bearbeitet wird. Moderne Touchdigitalisierer und Gaming-Peripheriegeräte können Eingaben für diese Ereignisse mit mindestens 100 Hz pro Zeiger melden, was bedeutet, dass Ihre App höchstens 100 Ereignisse durch Zeiger (oder Tastaturanschlag) pro Sekunde empfangen kann. Diese Aktualisierungsrate kann effektiv vervielfacht werden, wenn mehrere Zeigereingaben (oder Tastaturanschläge) gleichzeitig erfolgen. Die Ereignismeldungswarteschlange, die von der CoreDispatcher-Instanz für das CoreWindow-Objekt Ihrer App verarbeitet wird, wird unter Umständen SEHR schnell gefüllt.

Wenn die Anzeige Ihrer aktuellen Swapchain mit den Eingabeereignishandlern verknüpft ist, können Sie außerdem den Inhalt potenziell "überziehen", da die Eingabeereignisse viel häufiger als das Aktualisierungssignal des Ausgabegeräts eingehen. Dies hat zur Folge, dass einige Aktionen keinen Frame erhalten, der mit den Ergebnissen der jeweiligen Aktion aktualisiert wird, und der Bildschirm erst nach einer Verzögerung mit der Eingabe aktualisiert wird.

Wie gehen Sie also vor?

Die einfachste Herangehensweise ist das Abstimmen des Eingabeereignisses mit dem Ausgabeintervall durch Verwenden eines separaten Schleife oder eines separaten Timers. In optimierten Fällen ist dieser Timer annähernd mit dem Aktualisierungssignal für das Ausgabegerät synchronisiert (in der Regel 1/60 oder 1/120 einer Minute). Für unsere Zwecke verwenden wir jedoch eine einfache while-Schleife.

Im einfachsten Fall können Sie diese Schleife Ihrer Implementierung der IFrameworkView::Run-Methode hinzufügen. Sie testen damit den Status von CoreWindow: Ist es aktiviert, und ist es angedockt oder nicht? Wenn es deaktiviert oder angedockt ist, sollte die App nicht rendern und die Szene anzeigen. Die App kann bei dieser Gelegenheit auch ProcessEvents mithilfe von CoreProcessEventsOption::ProcessOneAndAllPending aufrufen, die ausstehenden Ereignisse ohne Benachrichtigung verteilen und dann zurück in einen Wartezustand versetzt werden.

Ist die CoreWindow-Instanz Ihrer App jedoch aktiviert, rufen Sie mithilfe von CoreProcessEventsOption::ProcessAllIfPresent die Funktion ProcessEvents auf, die wiederum die Handler für jedes Eingabeereignis aufruft, das sich derzeit in der Meldungswarteschlange befindet, seit ProcessEvents zuletzt aufgerufen wurde.

Der folgende Pseudocode veranschaulicht dieses Verfahren:

[Im folgenden Pseudocode ist "m_coreWindowClosed" ein boolescher Wert, der den Status des aktuellen CoreWindow-Objekts enthält, "m_renderNeeded" ist ein boolescher Wert, der angibt, ob gerendert werden sollte, und "m_updateState" ist der Status des Spiele-App-Prozesses in Bezug auf das App-Objekt selbst.]


void MyApp::Run()
{
	while (!m_coreWindowClosed)
	{
		switch (m_updateState)
		{
			case UpdateEngineState::Deactivated: // the app's process is not active (it is suspended)
			case UpdateEngineState::Snapped: // the app's window is snapped
				if (!m_renderNeeded)
				{
					CoreWindow::GetForCurrentThread()->Dispatcher->
						ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
					break;
				}
			default: // the app is active and not snapped
					CoreWindow::GetForCurrentThread()->Dispatcher->
						ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
					m_myGameObject->Render();
					m_myGameObject->PresentSwapChain(); // calls IDXGISwapChain::Present()
					m_renderNeeded = false;
				}
		}
	}
}

Wie bereits erwähnt, können Sie dies auch mit einem Timerobjekt durchführen, das Sie mit dem Aktualisierungssignal des Anzeigegeräts synchronisiert haben. Bei Verwendung der Run-Methode erstellen Sie ein Timerobjekt und registrieren dafür einen Rückruf im festgelegten Intervall. Anhand des Rückrufs können Sie den Status der CoreWindow-Instanz testen, das Spielobjekt abfragen, prüfen, ob ein Rendervorgang angefordert wurde und ProcessEvents mithilfe von CoreProcessEventsOption::ProcessAllIfPresent aufrufen.

Auch dieses Verfahren birgt natürlich einige Nachteile:

So wird z. B. ein bestimmter Prozentsatz von Eingabeereignismeldungen nicht verarbeitet. (Dieser Prozentsatz kann durch eine sehr enge Kopplung zwischen dem Timer und der maximalen Eingabeereignishäufigkeit reduziert werden.) In den meisten Fällen ist dies zulässig. Wenn Sie jedoch physische Spielaspekte auf einem separaten Timer mit hoher Genauigkeit haben und diese eine schnellere Aktualisierungsrate als die Anzeige erfordern, führt dies u. U. zu Problemen, und Sie müssen Ihr Physikmodell entsprechend kalibrieren. (Beispielsweise das Fahrtmodell in einem Rennsimulationsspiel.)

Bei diesem Verfahren tritt u. U. auch eine geringfügige Verzögerung bei der Eingabe auf. In diesem Fall werden Ereigniseingabemeldungen verworfen, während der aktuelle Ausgabeframe gerendert und angezeigt wird.

Zu guter Letzt sorgt dieses Modell dafür, dass bei Apps mit geringem Energieverbrauch die CPU durch die Eingabeereignisverarbeitung auch dann aktiv bleibt, wenn die CoreWindow-Instanz inaktiv ist und sich dadurch u. U. die Laufzeit des Geräteakkus verkürzt.

 

 

Anzeigen:
© 2016 Microsoft