Von NativeActivity migrieren Teil des Android Game Development Kit

Auf dieser Seite wird beschrieben, wie du in deinem Android-Spielprojekt von NativeActivity zu GameActivity migrierst.

GameActivity basiert auf NativeActivity aus dem Android-Framework und enthält Verbesserungen und neue Funktionen:

  • Unterstützt Fragment von Jetpack.
  • TextInput-Unterstützung hinzugefügt, um die Integration der Softtastatur zu vereinfachen.
  • Verarbeitet Touch- und Schlüsselereignisse in der Java-Klasse GameActivity und nicht in der Schnittstelle NativeActivity onInputEvent.

Vor der Migration empfehlen wir, den Startleitfaden zu lesen. Darin wird beschrieben, wie Sie GameActivity einrichten und in Ihr Projekt einbinden.

Aktualisierungen von JavaScript-Build-Skripts

GameActivity wird als Jetpack-Bibliothek verteilt. Wende die Schritte zum Aktualisieren des Gradle-Skripts an, die im Startleitfaden beschrieben sind:

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

    android.useAndroidX=true
    
  2. Sie können in der Datei gradle.properties auch eine vordefinierte Version angeben, z. B.:

    android.prefabVersion=2.0.0
    
  3. Aktivieren Sie die Funktion „Vorab“ 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 Ihr aktuelles mindestens unterstütztes API-Level weniger als 16 beträgt, aktualisieren Sie es auf mindestens 16.
    3. Aktualisieren Sie die kompilierte SDK-Version auf die Version, die für die games-activity-Bibliothek erforderlich ist. Jetpack benötigt normalerweise die neueste SDK-Version zum Zeitpunkt des Release-Builds.

    Ihre aktualisierte build.gradle-Datei könnte in etwa 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-Updates

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

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

  1. Erstellen Sie eine Kotlin- oder Java-Datei zum Hosten der neuen Startaktivität. Mit dem folgenden Code wird beispielsweise das MainActivity als Startaktivität erstellt und die native Hauptbibliothek libAndroidGame.so der Anwendung 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 App-Design im Vollbildmodus:

    <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 Design auf die Anwendung in der Datei AndroidManifest.xml an:

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

    Eine ausführliche Anleitung für den Vollbildmodus finden Sie im komplexen Leitfaden und im Repository für Spielebeispiele für die Implementierung.

Der Name der nativen Bibliothek wird durch diese Migrationsanleitung nicht geändert. Achten Sie bei Änderungen darauf, dass die Namen der nativen Bibliothek an den folgenden drei Standorten konsistent sind:

  • Kotlin- oder Java-Code:

    System.loadLibrary(“AndroidGame”)
    
  • AndroidManifest.xml:

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

    add_library(AndroidGame ...)
    

Aktualisierungen von Build-Scripts in C/C++

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

Die C/C++-Implementierung von GameActivity stellt einen Quellcode-Release bereit. Ab Version 1.2.2 wird eine statische Bibliotheksversion bereitgestellt. Die statische Bibliothek ist der empfohlene Releasetyp.

Der Release wird im AAE mit dem prefab-Dienstprogramm verpackt. Der native Code enthält die C/C++-Quellen von GameActivity und den native_app_glue-Code. Sie müssen zusammen mit dem C/C++-Code Ihrer Anwendung erstellt werden.

NativeActivity-Anwendungen verwenden bereits den in NDK enthaltenen native_app_glue-Code. Sie müssen sie durch die GameActivity-Version von native_app_glue ersetzen. Ansonsten gelten alle im Startleitfaden dokumentierten cmake-Schritte:

  • 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 Bibliothek game-activity in das Fertigungsmodul 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 das Paket game-activity in die Datei CMakeLists.txt Ihres Projekts und fügen Sie es dem Ziel hinzu. Für das game-activity-Paket ist libandroid.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)
    
  • Entfernen Sie alle Verweise auf den native_app_glue-Code von 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 den Quellcode-Release verwenden, schließen Sie die GameActivity-Quelldateien ein. Andernfalls können Sie diesen Schritt überspringen.

    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 dem UnzufriedenLinkError umgehen

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

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

Aktualisierungen des C/C++-Quellcodes

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

  • Verwende das native_app_glue, das mit GameActivity veröffentlicht wurde. Suchen und ersetzen Sie die gesamte android_native_app_glue.h-Nutzung durch:

    #include <game-activity/native_app_glue/android_native_app_glue.h>
    
  • Legen Sie sowohl den Bewegungsereignisfilter als auch den Schlüsselereignisfilter auf NULL fest, damit Ihre App Eingabeereignisse von allen Eingabegeräten empfangen kann. Normalerweise geschieht dies in der android_main()-Funktion:

    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 AInputEvent-bezogenen Code 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_pollAll(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;
    }
    
  • Prüfen und aktualisieren Sie die Logik, die an die AInputEvent von NativeActivity angehängt ist. Wie im vorherigen Schritt gezeigt, befindet sich die InputBuffer-Verarbeitung von GameActivity außerhalb der ALooper_pollAll()-Schleife.

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

Weitere Schritte

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

Wir empfehlen Ihnen, sich mit diesen Funktionen vertraut zu machen und sie für Ihre Spiele anzupassen.

Wenn Sie Fragen oder Empfehlungen zu GameActivity oder anderen AGDK-Bibliotheken haben, erstellen Sie einen Bug, um uns zu informieren.