Von NativeActivity migrieren   Teil des Android Game Development Kit.

Auf dieser Seite wird beschrieben, wie Sie in Ihrem Android-Spielprojekt von NativeActivity zu GameActivity migrieren.

GameActivity basiert auf NativeActivity aus dem Android-Framework und bietet Verbesserungen und neue Funktionen:

  • Unterstützt Fragment aus Jetpack.
  • Fügt TextInput-Unterstützung hinzu, um die Integration der Bildschirmtastatur zu erleichtern.
  • Verarbeitet Touch- und Tastaturereignisse in der Java-Klasse GameActivity anstelle der onInputEvent-Schnittstelle von NativeActivity.

Vor der Migration empfehlen wir Ihnen, die Anleitung für den Einstieg zu lesen. Dort wird beschrieben, wie Sie GameActivity in Ihrem Projekt einrichten und einbinden.

Java-Build-Skript aktualisieren

GameActivity wird als eine Jetpack-Bibliothek verteilt. Führen Sie die Schritte zum Aktualisieren des Gradle-Skripts aus, die in der Anleitung für den Einstieg beschrieben sind:

  1. Aktivieren Sie die Jetpack-Bibliothek in der Datei gradle.properties Ihres Projekts:

    android.useAndroidX=true
    
  2. Optional können Sie in derselben Datei gradle.properties eine Prefab-Version angeben, z. B.:

    android.prefabVersion=2.0.0
    
  3. Aktivieren Sie die Prefab-Funktion in der Datei build.gradle Ihrer App:

    android {
        ... // other configurations
        buildFeatures.prefab true
    }
    
  4. Fügen Sie Ihrer Anwendung die Abhängigkeit GameActivity hinzu:

    1. Fügen Sie die Bibliotheken core und games-activity hinzu.
    2. Wenn das aktuelle Mindest-API-Level unter 16 liegt, aktualisieren Sie es auf mindestens 16.
    3. Aktualisieren Sie die kompilierte SDK-Version auf die Version, die für die Bibliothek games-activity erforderlich ist. Jetpack erfordert in der Regel die neueste SDK-Version zum Zeitpunkt des Release-Builds.

    Ihre aktualisierte Datei build.gradle könnte so aussehen:

    android {
        compiledSdkVersion 33
        ... // other configurations.
        defaultConfig {
            minSdkVersion 16
        }
        ... // other configurations.
    
        buildFeatures.prefab true
    }
    dependencies {
        implementation 'androidx.core:core:1.9.0'
        implementation 'androidx.games:games-activity:1.2.2'
    }
    

Kotlin- oder Java-Code aktualisieren

NativeActivity kann als Startaktivität verwendet werden und erstellt eine Vollbildanwendung. Derzeit kann `GameActivity` nicht als Startaktivität verwendet werden. Apps müssen eine Klasse von GameActivity ableiten und diese als Startaktivität verwenden. Außerdem müssen Sie zusätzliche Konfigurationsänderungen vornehmen, um eine Vollbild-App zu erstellen.

Bei den folgenden Schritten wird davon ausgegangen, dass Ihre Anwendung NativeActivity als Startaktivität verwendet. Wenn das nicht der Fall ist, können Sie die meisten Schritte überspringen.

  1. Erstellen Sie eine Kotlin- oder Java-Datei, um die neue Startaktivität zu hosten. Mit dem folgenden Code wird beispielsweise MainActivity als Startaktivität erstellt und die native Hauptbibliothek der Anwendung, libAndroidGame.so, geladen:

    Kotlin

    class MainActivity : GameActivity() {
       override fun onResume() {
           super.onResume()
           // Use the function recommended from the following page:
           // https://d.android.com/training/system-ui/immersive
           hideSystemBars()
       }
       companion object {
           init {
               System.loadLibrary("AndroidGame")
           }
       }
    }

    Java

      public class MainActivity extends GameActivity {
          protected void onResume() {
              super.onResume();
              // Use the function recommended from
              // https://d.android.com/training/system-ui/immersive
              hideSystemBars();
          }
          static {
              System.loadLibrary("AndroidGame");
          }
      }
  2. Erstellen Sie in der Datei res\values\themes.xml ein Vollbild-App-Theme:

    <resources xmlns:tools="http://schemas.android.com/tools">
        <!-- Base application theme. -->
        <style name="Application.Fullscreen" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="android:windowFullscreen">true</item>
            <item name="android:windowContentOverlay">@null</item>"
        </style>
    </resources>
    
  3. Wenden Sie das Theme in der Datei AndroidManifest.xml auf die Anwendung an:

    <application  android:theme=”@style/Application.Fullscreen”>
         <!-- other configurations not listed here. -->
    </application>
    

    Eine detaillierte Anleitung zum Vollbildmodus finden Sie in der Anleitung zum immersiven Modus und in der Beispielimplementierung in dem games-samples-Repository.

In dieser Migrationsanleitung wird der Name der nativen Bibliothek nicht geändert. Wenn Sie ihn ändern, müssen die Namen der nativen Bibliothek an den folgenden drei Stellen übereinstimmen:

  • Kotlin- oder Java-Code:

    System.loadLibrary(AndroidGame)
    
  • AndroidManifest.xml:

    <meta-data android:name="android.app.lib_name"
            android:value="AndroidGame" />
    
  • In der Build-Skriptdatei für C/C++, z. B. CMakeLists.txt:

    add_library(AndroidGame ...)
    

C/C++-Build-Skript aktualisieren

In der Anleitung in diesem Abschnitt wird cmake als Beispiel verwendet. Wenn Ihre Anwendung verwendet ndk-build, müssen Sie die Befehle den entsprechenden Befehlen zuordnen, die auf der Dokumentationsseite zu ndk-build beschrieben sind.

Für die C/C++-Implementierung von GameActivity wurde ein Quellcode-Release bereitgestellt. Für Version 1.2.2 und höher wird ein Release der statischen Bibliothek bereitgestellt. Die statische Bibliothek ist der empfohlene Release-Typ.

Das Release wird mit dem prefab Dienstprogramm in das AAR-Paket gepackt. Der native Code enthält die C/C++-Quellen von GameActivity und den Code native_app_glue. Sie müssen zusammen mit dem C/C++-Code Ihrer Anwendung erstellt werden.

NativeActivity -Anwendungen verwenden bereits den Code native_app_glue, der im NDK enthalten ist. Sie müssen ihn durch die GameActivity-Version von native_app_glue ersetzen. Ansonsten gelten alle cmake-Schritte, die in der Anleitung für den Einstieg beschrieben sind:

  • Importieren Sie die statische C/C++-Bibliothek oder den C/C++-Quellcode wie folgt in Ihr Projekt:

    Statische Bibliothek

    Importieren Sie in der Datei CMakeLists.txt Ihres Projekts die statische Bibliothek game-activity in das Prefab-Modul game-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 Paket game-activity und fügen Sie es Ihrem Ziel hinzu. Für das Paket game-activity ist libandroid.so erforderlich. Wenn es fehlt, müssen Sie es auch importieren.

    find_package(game-activity REQUIRED CONFIG)
    ...
    target_link_libraries(... android game-activity::game-activity)
    
  • Entfernen Sie alle Verweise auf den Code native_app_glue des NDK, z. B.:

    ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c
        ...
    set(CMAKE_SHARED_LINKER_FLAGS
        "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
    
  • Wenn Sie das Quellcode-Release verwenden, fügen Sie die Quelldateien von GameActivity hinzu. Andernfalls überspringen Sie diesen Schritt.

    get_target_property(game-activity-include
                        game-activity::game-activity
                        INTERFACE_INCLUDE_DIRECTORIES)
    add_library(${PROJECT_NAME} SHARED
        main.cpp
        ${game-activity-include}/game-activity/native_app_glue/android_native_app_glue.c
        ${game-activity-include}/game-activity/GameActivity.cpp
        ${game-activity-include}/game-text-input/gametextinput.cpp)
    

Problem mit UnsatisfiedLinkError umgehen

Wenn ein UnsatsifiedLinkError für die com.google.androidgamesdk.GameActivity.initializeNativeCode() Funktion auftritt, fügen Sie diesen Code der Datei CMakeLists.txt hinzu:

set(CMAKE_SHARED_LINKER_FLAGS
    "${CMAKE_SHARED_LINKER_FLAGS} -u \
    Java_com_google_androidgamesdk_GameActivity_initializeNativeCode")

C/C++-Quellcode aktualisieren

Führen Sie die folgenden Schritte aus, um NativeActivity-Verweise in Ihrer Anwendung durch GameActivity zu ersetzen:

  • Verwenden Sie native_app_glue, das mit GameActivity veröffentlicht wurde. Suchen und ersetzen Sie alle Verwendungen von android_native_app_glue.h durch:

    #include <game-activity/native_app_glue/android_native_app_glue.h>
    
  • Setzen Sie sowohl den Motion-Event-Filter als auch den Schlüsselereignis-Filter auf NULL, damit Ihre App Eingabeereignisse von allen Eingabegeräten empfangen kann. Normalerweise tun Sie dies in der Funktion android_main():

    void android_main(android_app* app) {
        ... // other init code.
    
        android_app_set_key_event_filter(app, NULL);
        android_app_set_motion_event_filter(app, NULL);
    
        ... // additional init code, and game loop code.
    }
    
  • Entfernen Sie den Code, der sich auf AInputEvent bezieht, und ersetzen Sie ihn durch die InputBuffer-Implementierung von GameActivity:

    while (true) {
        // Read all pending events.
        int events;
        struct android_poll_source* source;
    
        // If not animating, block forever waiting for events.
        // If animating, loop until all events are read, then continue
        // to draw the next frame of animation.
        while ((ALooper_pollOnce(engine.animating ? 0 : -1, nullptr, &events,
                                (void**)&source)) >= 0) {
           // Process this app cycle or inset change event.
           if (source) {
               source->process(source->app, source);
           }
    
              ... // Other processing.
    
           // Check if app is exiting.
           if (state->destroyRequested) {
               engine_term_display(&engine);
               return;
           }
        }
        // Process input events if there are any.
        engine_handle_input(state);
    
       if (engine.animating) {
           // Draw a game frame.
       }
    }
    
    // Implement input event handling function.
    static int32_t engine_handle_input(struct android_app* app) {
       auto* engine = (struct engine*)app->userData;
       auto ib = android_app_swap_input_buffers(app);
       if (ib && ib->motionEventsCount) {
           for (int i = 0; i < ib->motionEventsCount; i++) {
               auto *event = &ib->motionEvents[i];
               int32_t ptrIdx = 0;
               switch (event->action & AMOTION_EVENT_ACTION_MASK) {
                   case AMOTION_EVENT_ACTION_POINTER_DOWN:
                   case AMOTION_EVENT_ACTION_POINTER_UP:
                       // Retrieve the index for the starting and the ending of any secondary pointers
                       ptrIdx = (event->action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
                                AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
                   case AMOTION_EVENT_ACTION_DOWN:
                   case AMOTION_EVENT_ACTION_UP:
                       engine->state.x = GameActivityPointerAxes_getAxisValue(
                           &event->pointers[ptrIdx], AMOTION_EVENT_AXIS_X);
                       engine->state.y = GameActivityPointerAxes_getAxisValue(
                           &event->pointers[ptrIdx], AMOTION_EVENT_AXIS_Y);
                       break;
                    case AMOTION_EVENT_ACTION_MOVE:
                    // Process the move action: the new coordinates for all active touch pointers
                    // are inside the event->pointers[]. Compare with our internally saved
                    // coordinates to find out which pointers are actually moved. Note that there is
                    // no index embedded inside event->action for AMOTION_EVENT_ACTION_MOVE (there
                    // might be multiple pointers moved at the same time).
                        ...
                       break;
               }
           }
           android_app_clear_motion_events(ib);
       }
    
       // Process the KeyEvent in a similar way.
           ...
    
       return 0;
    }
    
  • Überprüfen und aktualisieren Sie die Logik, die an AInputEvent von NativeActivity angehängt ist. Wie im vorherigen Schritt gezeigt, erfolgt die Verarbeitung von InputBuffer von GameActivity außerhalb der ALooper_pollOnce()-Schleife.

  • Ersetzen Sie die Verwendung von android_app::activity->clazz durch android_app:: activity->javaGameActivity. GameActivity benennt die Java-Instanz GameActivity um.

Weitere Schritte

Die vorherigen Schritte decken die Funktionalität von NativeActivity ab. GameActivity bietet jedoch zusätzliche Funktionen, die Sie möglicherweise verwenden möchten:

Wir empfehlen Ihnen, diese Funktionen zu testen und sie gegebenenfalls in Ihre Spiele zu übernehmen.

Wenn Sie Fragen oder Empfehlungen zu GameActivity oder anderen AGDK Bibliotheken haben, erstellen Sie einen Fehlerbericht.