Effectuer une migration depuis NativeActivity   composante de l'Android Game Development Kit

Cette page explique comment migrer de NativeActivity vers GameActivity dans votre projet de jeu Android.

GameActivity est basé sur NativeActivity, du framework Android, mais offre plusieurs améliorations et nouvelles fonctionnalités :

  • Compatible avec l'élément Fragment, de Jetpack.
  • Compatible avec TextInput pour faciliter l'intégration des claviers virtuels.
  • Gère les événements associés aux gestes tactiles et aux touches dans la classe Java GameActivity plutôt que dans l'interface onInputEvent de NativeActivity.

Avant de procéder à la migration, nous vous recommandons de lire le guide de démarrage, qui explique comment configurer et intégrer GameActivity à votre projet.

Mises à jour du script de compilation Java

GameActivity est distribué en tant que bibliothèque Jetpack. Veillez à appliquer la procédure de mise à jour du script Gradle décrite dans le guide de démarrage :

  1. Activez la bibliothèque Jetpack dans le fichier gradle.properties de votre projet :

    android.useAndroidX=true
    
  2. Vous pouvez également spécifier une version de Prefab dans le même fichier gradle.properties, par exemple :

    android.prefabVersion=2.0.0
    
  3. Activez la fonctionnalité Prefab dans le fichier build.gradle de votre application :

    android {
        ... // other configurations
        buildFeatures.prefab true
    }
    
  4. Ajoutez la dépendance GameActivity à votre application :

    1. Ajoutez les bibliothèques core et games-activity.
    2. Si votre niveau d'API minimal actuellement accepté est inférieur à 16, procédez à sa mise à jour vers cette version ou une version ultérieure.
    3. Mettez à jour la version du SDK compilée pour obtenir celle qui est requise par la bibliothèque games-activity. Jetpack nécessite généralement la dernière version du SDK au moment de la compilation de la version.

    Votre fichier build.gradle mis à jour peut se présenter comme suit :

    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'
    }
    

Mises à jour du code Kotlin ou Java

L'élément NativeActivity peut être utilisé comme activité de démarrage et crée une application plein écran. Actuellement, il est impossible d'utiliser GameActivity comme activité de démarrage. Les applications doivent créer une classe dérivée de GameActivity et l'utiliser comme activité de démarrage. Vous devez également apporter des modifications de configuration supplémentaires pour créer une application plein écran.

Les étapes suivantes supposent que votre application utilise NativeActivity comme activité de démarrage. Si ce n'est pas le cas, vous pouvez en ignorer la majorité.

  1. Créez un fichier Kotlin ou Java pour héberger la nouvelle activité de démarrage. Par exemple, le code suivant crée MainActivity en tant qu'activité de démarrage et charge la principale bibliothèque native de l'application, libAndroidGame.so :

    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. Créez un thème d'application en plein écran dans le fichier res\values\themes.xml :

    <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. Appliquez le thème à l'application dans le fichier AndroidManifest.xml :

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

    Pour obtenir des instructions détaillées sur le mode plein écran, consultez le guide immersif et l'exemple d'implémentation qui figure dans le dépôt game-samples.

Ce guide de migration ne modifie pas le nom de la bibliothèque native. Si vous décidez de le modifier, assurez-vous que les noms des bibliothèques natives sont les mêmes aux trois emplacements suivants :

  • Code Kotlin ou Java :

    System.loadLibrary(AndroidGame)
    
  • AndroidManifest.xml :

    <meta-data android:name="android.app.lib_name"
            android:value="AndroidGame" />
    
  • Dans le fichier de script de compilation C/C++, par exemple CMakeLists.txt :

    add_library(AndroidGame ...)
    

Mises à jour de script de compilation C/C++

Les instructions de cette section utilisent cmake comme exemple. Si votre application utilise ndk-build, ajustez ces instructions avec commandes équivalentes décrites à la page de documentation de ndk-build.

L'implémentation C/C++ de GameActivity fournit une version de code source. À partir de la version 1.2.2, une version de bibliothèque statique est fournie. La bibliothèque statique est le type de version recommandé.

La version est intégrée à l'AAR avec l'utilitaire prefab. Le code natif inclut les sources C/C++ de GameActivity et le code native_app_glue. Ils doivent être compilés avec le code C/C++ de votre application.

Les applications NativeActivity utilisent déjà le code native_app_glue présent dans NDK. Vous devez le remplacer par la version de native_app_glue de GameActivity. En dehors de cela, toutes les opérations cmake décrites dans le guide de démarrage s'appliquent :

  • Importez la bibliothèque statique C/C++ ou le code source C/++ dans votre projet comme suit.

    Bibliothèque statique

    Dans le fichier CMakeLists.txt de votre projet, importez la bibliothèque statique game-activity dans le module Prefab game-activity_static :

    find_package(game-activity REQUIRED CONFIG)
    target_link_libraries(${PROJECT_NAME} PUBLIC log android
    game-activity::game-activity_static)
    

    Code source

    Dans le fichier CMakeLists.txt de votre projet, importez le package game-activity et ajoutez-le à votre cible. Le package game-activity nécessite libandroid.so. S'il est absent, vous devez donc également l'importer.

    find_package(game-activity REQUIRED CONFIG)
    ...
    target_link_libraries(... android game-activity::game-activity)
    
  • Supprimez toutes les références au code native_app_glue de NDK, telles que :

    ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c
        ...
    set(CMAKE_SHARED_LINKER_FLAGS
        "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
    
  • Si vous utilisez la version du code source, incluez les fichiers sources GameActivity. Sinon, ignorez cette étape.

    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)
    

Résoudre une erreur UnsatisfactionLinkError

Si vous rencontrez une UnsatsifiedLinkError pour la fonction com.google.androidgamesdk.GameActivity.initializeNativeCode(), ajoutez ce code à votre fichier CMakeLists.txt :

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

Mises à jour du code source C/C++

Procédez comme suit pour remplacer les références NativeActivity dans votre application par GameActivity :

  • Utilisez l'élément native_app_glue publié avec GameActivity. Recherchez et remplacez toute utilisation de android_native_app_glue.h par :

    #include <game-activity/native_app_glue/android_native_app_glue.h>
    
  • Définissez le filtre d'événement de mouvement et le filtre d'événement de touche sur NULL pour que votre application puisse recevoir les événements, quel que soit le périphérique d'entrée. Cette opération s'effectue généralement dans la fonction 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.
    }
    
  • Supprimez le code associé AInputEvent, puis remplacez-le par l'implémentation InputBuffer de 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;
    }
    
  • Examinez et mettez à jour la logique associée au AInputEvent d'activité native. Comme indiqué à l'étape précédente, le traitement InputBuffer de GameActivity n'est pas exécuté dans la boucle ALooper_pollAll().

  • Remplacez l'utilisation de android_app::activity->clazz par android_app:: activity->javaGameActivity. GameActivity renomme l'instance Java GameActivity.

Étapes supplémentaires :

Les opérations précédentes décrivent les fonctionnalités de NativeActivity, mais GameActivity propose des fonctionnalités supplémentaires qui peuvent vous être utiles :

Nous vous recommandons d'essayer ces fonctionnalités et de les adopter si elles sont appropriées pour vos jeux.

Si vous avez des questions ou des recommandations concernant GameActivity ou d'autres bibliothèques AGDK, créez un rapport de bug pour nous en informer.