Erste Schritte mit GameActivity Teil des Android Game Development Kit.
In diesem Leitfaden wird beschrieben, wie du GameActivity
einrichtest und einbindest und Ereignisse in deinem Android-Spiel verarbeiten kannst.
GameActivity
vereinfacht die Verwendung kritischer APIs und hilft dir dabei, dein C- oder C++-Spiel für Android zu nutzen.
Bisher war NativeActivity
die empfohlene Klasse für Spiele. GameActivity
ersetzt sie als empfohlene Klasse für Spiele und ist abwärtskompatibel mit API-Level 19.
Ein Beispiel, in das GameActivity integriert wird, finden Sie im games-sample-Repository.
Vorbereitung
Informationen zum Abrufen einer Distribution finden Sie unter GameActivity
-Releases.
Build einrichten
Unter Android dient ein Activity
als Einstiegspunkt für Ihr Spiel und stellt auch den Window
zum Zeichnen bereit. Viele Spiele erweitern diese Activity
um eine eigene Java- oder Kotlin-Klasse, um Einschränkungen in NativeActivity
zu umgehen, und verwenden JNI
-Code, um eine Verbindung zu ihrem C- oder C++-Spielcode herzustellen.
GameActivity
bietet die folgenden Funktionen:
Übernimmt von
AppCompatActivity
, sodass Sie die Android Jetpack-Architekturkomponenten verwenden können.Wird in ein
SurfaceView
-Element gerendert, mit dem du eine Oberfläche mit jedem anderen Android-UI-Element erstellen kannst.Verarbeitet Java-Aktivitätsereignisse. Dadurch kann jedes Android-UI-Element (z. B.
EditText
,WebView
oderAd
) über eine C-Schnittstelle in dein Spiel integriert werden.Bietet eine C API ähnlich wie
NativeActivity
und eineandroid_native_app_glue
-Bibliothek.
GameActivity
wird als Android Archive (AAR) bereitgestellt. Dieses AAR enthält die Java-Klasse, die Sie in AndroidManifest.xml
verwenden, sowie den C- und C++-Quellcode, der die Java-Seite von GameActivity
mit der C/C++-Implementierung der Anwendung verbindet. Wenn Sie GameActivity
1.2.2 oder höher verwenden, ist auch die statische C/C++-Bibliothek verfügbar. Wir empfehlen, nach Möglichkeit die statische Bibliothek anstelle des Quellcodes zu verwenden.
Binden Sie diese Quelldateien oder die statische Bibliothek als Teil des Build-Prozesses über Prefab
ein. Dadurch werden native Bibliotheken und Quellcode in Ihrem CMake-Projekt oder NDK-Build verfügbar gemacht.
Folgen Sie der Anleitung auf der Seite Jetpack Android Games, um die Abhängigkeit der
GameActivity
-Bibliothek in die Dateibuild.gradle
Ihres Spiels einzufügen.Aktivieren Sie die Vorabfertigung, indem Sie mit der Android-Plug-in-Version (AGP) 4.1 oder höher Folgendes tun:
- Fügen Sie Folgendes in den
android
-Block der Dateibuild.gradle
Ihres Moduls ein:
buildFeatures { prefab true }
- Wählen Sie eine vordefinierte Version aus und legen Sie sie auf die Datei
gradle.properties
fest:
android.prefabVersion=2.0.0
Wenn Sie ältere AGP-Versionen verwenden, finden Sie die entsprechende Konfigurationsanleitung in der Prefab-Dokumentation.
- Fügen Sie Folgendes in den
Importieren Sie entweder die statische C/C++-Bibliothek oder den 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 Fertigungsmodulgame-activity_static
:find_package(game-activity REQUIRED CONFIG) target_link_libraries(${PROJECT_NAME} PUBLIC log android game-activity::game-activity_static)
Quellcode
Importieren Sie das Paket
game-activity
in die DateiCMakeLists.txt
Ihres Projekts und fügen Sie es dem Ziel hinzu. Für dasgame-activity
-Paket istlibandroid.so
erforderlich. Wenn es fehlt, müssen Sie es also ebenfalls importieren.find_package(game-activity REQUIRED CONFIG) ... target_link_libraries(... android game-activity::game-activity)
Fügen Sie außerdem die folgenden Dateien in die Datei
CmakeLists.txt
Ihres Projekts ein:GameActivity.cpp
,GameTextInput.cpp
undandroid_native_app_glue.c
.
So startet Android deine Aktivitäten
Das Android-System führt Code in Ihrer Activity-Instanz aus, indem es Callback-Methoden aufruft, die bestimmten Phasen des Aktivitätslebenszyklus entsprechen. Damit Android deine Aktivitäten starten und dein Spiel starten kann, musst du deine Aktivitäten mit den entsprechenden Attributen im Android-Manifest deklarieren. Weitere Informationen finden Sie unter Einführung in Aktivitäten.
Android-Manifest
Für jedes App-Projekt muss die Datei AndroidManifest.xml im Stammverzeichnis des Projektquellsatzes vorhanden sein. Die Manifestdatei beschreibt wichtige Informationen zu deiner App für die Android-Build-Tools, das Android-Betriebssystem und Google Play. Dies ist z. B. in folgenden Situationen der Fall:
Paketname und App-ID zur eindeutigen Identifizierung Ihres Spiels bei Google Play.
App-Komponenten wie Aktivitäten, Dienste, Übertragungsempfänger und Inhaltsanbieter
Berechtigungen für den Zugriff auf geschützte Teile des Systems oder andere Apps
Unter Gerätekompatibilität legen Sie die Hardware- und Softwareanforderungen für Ihr Spiel fest.
Name der nativen Bibliothek für
GameActivity
undNativeActivity
(Standard ist libmain.so).
GameActivity in Ihrem Spiel implementieren
Erstellen oder identifizieren Sie die Java-Hauptaktivitätsklasse, die im
activity
-Element derAndroidManifest.xml
-Datei angegeben ist. Ändern Sie diese Klasse, umGameActivity
aus demcom.google.androidgamesdk
-Paket zu erweitern:import com.google.androidgamesdk.GameActivity; public class YourGameActivity extends GameActivity { ... }
Achten Sie darauf, dass Ihre native Bibliothek zu Beginn 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 Ihr Bibliotheksname nicht dem Standardnamen (libmain.so
) entspricht:<meta-data android:name="android.app.lib_name" android:value="android-game" />
android_main implementieren
Die
android_native_app_glue
-Bibliothek ist eine Quellcodebibliothek, mit der dein SpielGameActivity
-Lebenszyklusereignisse in einem separaten Thread verwaltet, um eine Blockierung in deinem Hauptthread zu verhindern. Wenn Sie die Bibliothek verwenden, registrieren Sie den Callback zur Verarbeitung von Lebenszyklusereignissen wie Touch-Eingaben. DasGameActivity
-Archiv enthält eine eigene Version derandroid_native_app_glue
-Bibliothek, sodass Sie nicht die in NDK-Releases enthaltene Version verwenden können. Wenn Ihre Spiele dieandroid_native_app_glue
-Bibliothek verwenden, die im NDK enthalten ist, wechseln Sie zur VersionGameActivity
.Nachdem Sie Ihrem Projekt den Quellcode der
android_native_app_glue
-Bibliothek hinzugefügt haben, interagiert es mitGameActivity
. Implementieren Sie eine Funktion namensandroid_main
, die von der Bibliothek aufgerufen und als Einstiegspunkt für Ihr Spiel verwendet wird. Es wird eine Struktur mit dem Namenandroid_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. das Abfragen und Verarbeiten von App-Zyklusereignissen, die in NativeAppGlueAppCmd definiert sind. Das folgende Snippet registriert beispielsweise die Funktion_hand_cmd_proxy
alsNativeAppGlueAppCmd
-Handler, fragt dann App-Zyklusereignisse ab und sendet sie zur Verarbeitung an den registrierten Handler(inandroid_app::onAppCmd
):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_pollAll(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 mit Ereignissen umgegangen wird (siehe nächster Abschnitt).
Ereignisse verarbeiten
Damit Eingabeereignisse Ihre App erreichen, müssen Sie Ereignisfilter mit android_app_set_motion_event_filter
und android_app_set_key_event_filter
erstellen und registrieren.
Standardmäßig lässt die native_app_glue
-Bibliothek nur Bewegungsereignisse aus der SOURCE_TOUCHSCREEN-Eingabe zu. Weitere Informationen finden Sie in der Referenzdokumentation und im android_native_app_glue
-Implementierungscode.
Rufe zum Verarbeiten von Eingabeereignissen einen Verweis auf android_input_buffer
mit android_app_swap_input_buffers()
in deiner Spielschleife ab. Sie enthalten Bewegungsereignisse und Schlüsselereignisse, die seit der letzten Abfrage aufgetreten sind. Die Anzahl der enthaltenen Ereignisse wird in motionEventsCount
bzw. keyEventsCount
gespeichert.
Jedes Ereignis in der Spielschleife iterieren und verarbeiten In diesem Beispiel iteriert der folgende Code
motionEvents
und verarbeitet sie überhandle_event
: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 weiterer zugehöriger Funktionen finden Sie im GitHub-Beispiel.Wenn Sie fertig sind, denken Sie daran, die Warteschlange der Ereignisse zu löschen, die Sie gerade behandelt haben:
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
- Referenzdokumentation zu GameActivity
- GameActivity-Implementierung
Wenn Sie Fehler melden oder neue Funktionen für GameActivity anfordern möchten, verwenden Sie die GameActivity-Problemverfolgung.