Erste Schritte mit GameActivity Teil des Android Game Development Kit
In diesem Leitfaden wird beschrieben, wie Sie GameActivity
einrichten und in Ihr Android-Spiel einbinden und wie Sie Ereignisse verarbeiten.
GameActivity
vereinfacht die Verwendung wichtiger APIs und hilft Ihnen so, Ihr C- oder C++-Spiel auf Android zu übertragen.
Bisher war NativeActivity
die empfohlene Klasse für Spiele. GameActivity
ersetzt sie als empfohlene Klasse für Spiele und ist abwärtskompatibel bis API‑Level 19.
Ein Beispiel für die Integration von GameActivity finden Sie im games-samples-Repository.
Vorbereitung
Eine Distribution finden Sie unter GameActivity
-Releases.
Build einrichten
Unter Android dient ein Activity
als Einstiegspunkt für Ihr Spiel und stellt auch das Window
zum Zeichnen bereit. Viele Spiele erweitern diese Activity
mit einer eigenen Java- oder Kotlin-Klasse, um Einschränkungen in NativeActivity
zu umgehen, während sie JNI
-Code verwenden, um eine Verbindung zu ihrem C- oder C++-Spielcode herzustellen.
GameActivity
bietet die folgenden Funktionen:
Wird von
AppCompatActivity
abgeleitet, sodass Sie Android Jetpack Architecture Components verwenden können.Wird als
SurfaceView
gerendert, über das Sie mit anderen Android-UI-Elementen interagieren können.Verarbeitet Java-Aktivitätsereignisse. So kann jedes Android-UI-Element (z. B. ein
EditText
, einWebView
oder einAd
) über eine C-Schnittstelle in Ihr Spiel eingebunden werden.Bietet eine C-API, die der
NativeActivity
- und derandroid_native_app_glue
-Bibliothek ähnelt.
GameActivity
wird als Android-Archiv (AAR) bereitgestellt. Dieses AAR enthält die Java-Klasse, die Sie in Ihrem AndroidManifest.xml
verwenden, sowie den C- und C++-Quellcode, der die Java-Seite von GameActivity
mit der C/C++-Implementierung der App verbindet. Wenn Sie GameActivity
1.2.2 oder höher verwenden, wird auch die statische C/C++-Bibliothek bereitgestellt. Wir empfehlen, nach Möglichkeit die statische Bibliothek anstelle des Quellcodes zu verwenden.
Nehmen Sie diese Quelldateien oder die statische Bibliothek als Teil Ihres Build-Prozesses über Prefab
auf. Dadurch werden native Bibliotheken und Quellcode für Ihr CMake-Projekt oder Ihren NDK-Build verfügbar gemacht.
Folgen Sie der Anleitung auf der Seite Jetpack Android Games, um die
GameActivity
-Bibliotheksabhängigkeit derbuild.gradle
-Datei Ihres Spiels hinzuzufügen.So aktivieren Sie Prefab mit Android-Plug-in-Version (AGP) 4.1 und höher:
- Fügen Sie dem
android
-Block der Dateibuild.gradle
Ihres Moduls Folgendes hinzu:
buildFeatures { prefab true }
- Wählen Sie eine Prefab-Version aus und legen Sie sie für die Datei
gradle.properties
fest:
android.prefabVersion=2.0.0
Wenn Sie frühere AGP-Versionen verwenden, folgen Sie der Prefab-Dokumentation für die entsprechenden Konfigurationsanleitungen.
- Fügen Sie dem
Importieren Sie die statische C/C++-Bibliothek oder den C/C++-Quellcode wie unten beschrieben in Ihr Projekt.
Statische Bibliothek
Importieren Sie in der Datei
CMakeLists.txt
Ihres Projekts die statische Bibliothekgame-activity
in das Prefab-Modulgame-activity_static
:find_package(game-activity REQUIRED CONFIG) target_link_libraries(${PROJECT_NAME} PUBLIC log android game-activity::game-activity_static)
Quellcode
Importieren Sie in der Datei
CMakeLists.txt
Ihres Projekts das Paketgame-activity
und fügen Sie es Ihrem Ziel hinzu. Für das Paketgame-activity
istlibandroid.so
erforderlich. Wenn es fehlt, müssen Sie es also auch importieren.find_package(game-activity REQUIRED CONFIG) ... target_link_libraries(... android game-activity::game-activity)
Fügen Sie außerdem die folgenden Dateien in das
CmakeLists.txt
Ihres Projekts ein:GameActivity.cpp
,GameTextInput.cpp
undandroid_native_app_glue.c
.
So startet Android Ihre Aktivität
Das Android-System führt Code in Ihrer Activity-Instanz aus, indem es Callback-Methoden aufruft, die bestimmten Phasen des Activity-Lebenszyklus entsprechen. Damit Android Ihre Aktivität starten und Ihr Spiel ausführen kann, müssen Sie Ihre Aktivität mit den entsprechenden Attributen im Android-Manifest deklarieren. Weitere Informationen finden Sie unter Einführung in Aktivitäten.
Android-Manifest
Jedes App-Projekt muss eine AndroidManifest.xml-Datei im Stammverzeichnis des Projektquellsatzes haben. Die Manifestdatei enthält wichtige Informationen zu Ihrer App für die Android-Build-Tools, das Android-Betriebssystem und Google Play. Sie beinhalten die folgenden Funktionen:
Paketname und App-ID zur eindeutigen Identifizierung Ihres Spiels bei Google Play.
App-Komponenten wie Aktivitäten, Dienste, Broadcast-Empfänger und Contentanbieter.
Berechtigungen für den Zugriff auf geschützte Teile des Systems oder andere Apps.
Gerätekompatibilität, um Hardware- und Softwareanforderungen für Ihr Spiel anzugeben.
Name der nativen Bibliothek für
GameActivity
undNativeActivity
(Standard ist libmain.so).
GameActivity in Ihrem Spiel implementieren
Erstellen oder identifizieren Sie die Java-Klasse Ihrer Hauptaktivität (die im
activity
-Element in IhrerAndroidManifest.xml
-Datei angegeben ist). Ändern Sie diese Klasse so, dass sieGameActivity
aus dem Paketcom.google.androidgamesdk
erweitert:import com.google.androidgamesdk.GameActivity; public class YourGameActivity extends GameActivity { ... }
Achten Sie darauf, dass Ihre native Bibliothek am Anfang mit einem statischen Block geladen wird:
public class EndlessTunnelActivity extends GameActivity { static { // Load the native library. // The name "android-game" depends on your CMake configuration, must be // consistent here and inside AndroidManifect.xml System.loadLibrary("android-game"); } ... }
Fügen Sie Ihre native Bibliothek zu
AndroidManifest.xml
hinzu, wenn der Name Ihrer Bibliothek nicht der Standardname (libmain.so
) ist:<meta-data android:name="android.app.lib_name" android:value="android-game" />
android_main implementieren
Die
android_native_app_glue
-Bibliothek ist eine Quellcodebibliothek, die von Ihrem Spiel verwendet wird, umGameActivity
-Lifecycle-Ereignisse in einem separaten Thread zu verwalten und so Blockierungen im Hauptthread zu verhindern. Wenn Sie die Bibliothek verwenden, registrieren Sie den Callback, um Lifecycle-Ereignisse wie Touch-Eingabeereignisse zu verarbeiten. DasGameActivity
-Archiv enthält eine eigene Version derandroid_native_app_glue
-Bibliothek. Sie können also nicht die Version verwenden, die in NDK-Releases enthalten ist. Wenn Ihre Spiele die im NDK enthalteneandroid_native_app_glue
-Bibliothek verwenden, wechseln Sie zurGameActivity
-Version.Nachdem Sie den Quellcode der
android_native_app_glue
-Bibliothek zu Ihrem Projekt hinzugefügt haben, wird er mitGameActivity
verknüpft. Implementieren Sie eine Funktion namensandroid_main
, die von der Bibliothek aufgerufen wird und als Einstiegspunkt für Ihr Spiel dient. Es wird eine Struktur namensandroid_app
übergeben. Das kann je nach Spiel und Engine variieren. Beispiel:#include <game-activity/native_app_glue/android_native_app_glue.h> extern "C" { void android_main(struct android_app* state); }; void android_main(struct android_app* app) { NativeEngine *engine = new NativeEngine(app); engine->GameLoop(); delete engine; }
Verarbeiten Sie
android_app
in Ihrer Hauptspielschleife, z. B. durch Abrufen und Verarbeiten von App-Zyklusereignissen, die in NativeAppGlueAppCmd definiert sind. Im folgenden Snippet wird beispielsweise die Funktion_hand_cmd_proxy
alsNativeAppGlueAppCmd
-Handler registriert. Anschließend werden App-Zyklusereignisse abgefragt und zur Verarbeitung an den registrierten Handler(inandroid_app::onAppCmd
) gesendet:void NativeEngine::GameLoop() { mApp->userData = this; mApp->onAppCmd = _handle_cmd_proxy; // register your command handler. mApp->textInputState = 0; while (1) { int events; struct android_poll_source* source; // If not animating, block until we get an event; // If animating, don't block. while ((ALooper_pollOnce(IsAnimating() ? 0 : -1, NULL, &events, (void **) &source)) >= 0) { if (source != NULL) { // process events, native_app_glue internally sends the outstanding // application lifecycle events to mApp->onAppCmd. source->process(source->app, source); } if (mApp->destroyRequested) { return; } } if (IsAnimating()) { DoFrame(); } } }
Weitere Informationen finden Sie in der Implementierung des NDK-Beispiels Endless Tunnel. Der Hauptunterschied besteht darin, wie Ereignisse behandelt werden. Das wird im nächsten Abschnitt beschrieben.
Ereignisse verarbeiten
Damit Eingabeereignisse Ihre App erreichen, müssen Sie Ihre Ereignisfilter mit android_app_set_motion_event_filter
und android_app_set_key_event_filter
erstellen und registrieren.
Standardmäßig sind in der native_app_glue
-Bibliothek nur Bewegungsereignisse von der Eingabequelle SOURCE_TOUCHSCREEN zulässig. Weitere Informationen finden Sie in der Referenzdokumentation und im android_native_app_glue
-Implementierungscode.
Um Eingabeereignisse zu verarbeiten, rufen Sie in Ihrer Spielschleife mit android_app_swap_input_buffers()
einen Verweis auf android_input_buffer
ab. Sie enthalten Bewegungsereignisse und Schlüsselereignisse, die seit dem letzten Abruf aufgetreten sind. Die Anzahl der enthaltenen Ereignisse wird in motionEventsCount
bzw. keyEventsCount
gespeichert.
Jedes Ereignis in Ihrer Spielschleife durchlaufen und verarbeiten In diesem Beispiel werden die
motionEvents
mit dem folgenden Code durchlaufen und überhandle_event
verarbeitet:android_input_buffer* inputBuffer = android_app_swap_input_buffers(app); if (inputBuffer && inputBuffer->motionEventsCount) { for (uint64_t i = 0; i < inputBuffer->motionEventsCount; ++i) { GameActivityMotionEvent* motionEvent = &inputBuffer->motionEvents[i]; if (motionEvent->pointerCount > 0) { const int action = motionEvent->action; const int actionMasked = action & AMOTION_EVENT_ACTION_MASK; // Initialize pointerIndex to the max size, we only cook an // event at the end of the function if pointerIndex is set to a valid index range uint32_t pointerIndex = GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT; struct CookedEvent ev; memset(&ev, 0, sizeof(ev)); ev.motionIsOnScreen = motionEvent->source == AINPUT_SOURCE_TOUCHSCREEN; if (ev.motionIsOnScreen) { // use screen size as the motion range ev.motionMinX = 0.0f; ev.motionMaxX = SceneManager::GetInstance()->GetScreenWidth(); ev.motionMinY = 0.0f; ev.motionMaxY = SceneManager::GetInstance()->GetScreenHeight(); } switch (actionMasked) { case AMOTION_EVENT_ACTION_DOWN: pointerIndex = 0; ev.type = COOKED_EVENT_TYPE_POINTER_DOWN; break; case AMOTION_EVENT_ACTION_POINTER_DOWN: pointerIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); ev.type = COOKED_EVENT_TYPE_POINTER_DOWN; break; case AMOTION_EVENT_ACTION_UP: pointerIndex = 0; ev.type = COOKED_EVENT_TYPE_POINTER_UP; break; case AMOTION_EVENT_ACTION_POINTER_UP: pointerIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); ev.type = COOKED_EVENT_TYPE_POINTER_UP; break; case AMOTION_EVENT_ACTION_MOVE: { // Move includes all active pointers, so loop and process them here, // we do not set pointerIndex since we are cooking the events in // this loop rather than at the bottom of the function ev.type = COOKED_EVENT_TYPE_POINTER_MOVE; for (uint32_t i = 0; i < motionEvent->pointerCount; ++i) { _cookEventForPointerIndex(motionEvent, callback, ev, i); } break; } default: break; } // Only cook an event if we set the pointerIndex to a valid range, note that // move events cook above in the switch statement. if (pointerIndex != GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT) { _cookEventForPointerIndex(motionEvent, callback, ev, pointerIndex); } } } android_app_clear_motion_events(inputBuffer); }
Die Implementierung von
_cookEventForPointerIndex()
und anderen zugehörigen Funktionen finden Sie im GitHub-Beispiel.Wenn Sie fertig sind, müssen Sie die Warteschlange der gerade verarbeiteten Ereignisse leeren:
android_app_clear_motion_events(mApp);
Zusätzliche Ressourcen
Weitere Informationen zu GameActivity
finden Sie hier:
- Versionshinweise zu GameActivity und AGDK
- GameTextInput in GameActivity verwenden
- Migrationsanleitung für NativeActivity
- GameActivity-Referenzdokumentation
- GameActivity-Implementierung:
Wenn Sie Fehler melden oder neue Funktionen für GameActivity anfordern möchten, verwenden Sie die GameActivity-Problemverfolgung.