Esegui la migrazione da NativeActivity Parte del Game Development Kit per Android.

In questa pagina viene descritto come eseguire la migrazione da NativeActivity a GameActivity nel tuo progetto di gioco per Android.

GameActivity è basato su NativeActivity del framework Android, con miglioramenti e nuove funzionalità:

  • Supporta Fragment di Jetpack.
  • Aggiunge il supporto TextInput per facilitare l'integrazione della tastiera software.
  • Gestisce gli eventi tocco e chiave nella classe Java GameActivity anziché nell'interfaccia onInputEvent di NativeActivity.

Prima di eseguire la migrazione, ti consigliamo di leggere la guida introduttiva, che descrive come configurare e integrare GameActivity nel tuo progetto.

Aggiornamenti degli script di build Java

GameActivity viene distribuito come libreria Jetpack. Assicurati di applicare i passaggi per l'aggiornamento dello script Gradle descritti nella guida introduttiva:

  1. Attiva la libreria Jetpack nel file gradle.properties del tuo progetto:

    android.useAndroidX=true
    
  2. Se vuoi, specifica una versione prefabbricata nello stesso file gradle.properties, ad esempio:

    android.prefabVersion=2.0.0
    
  3. Attiva la funzionalità prefabbricata nel file build.gradle dell'app:

    android {
        ... // other configurations
        buildFeatures.prefab true
    }
    
  4. Aggiungi la dipendenza GameActivity alla tua applicazione:

    1. Aggiungi le librerie core e games-activity.
    2. Se l'attuale livello API minimo supportato è inferiore a 16, aggiornalo ad almeno 16.
    3. Aggiorna la versione dell'SDK compilata con quella richiesta dalla libreria games-activity. Jetpack richiede in genere la versione più recente dell'SDK al momento della build della release.

    Il file build.gradle aggiornato potrebbe avere un aspetto simile al seguente:

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

Aggiornamenti del codice Kotlin o Java

NativeActivity può essere utilizzato come attività di avvio e crea un'applicazione a schermo intero. Al momento, GameActivity non può essere utilizzato come attività di avvio. Le app devono derivare una classe da GameActivity e utilizzarla come attività di avvio. Devi anche apportare ulteriori modifiche alla configurazione per creare un'app a schermo intero.

I passaggi seguenti presuppongono che l'applicazione utilizzi NativeActivity come attività di avvio. In caso contrario, puoi saltare la maggior parte di questi passaggi.

  1. Crea un file Kotlin o Java per ospitare la nuova attività di avvio. Ad esempio, il seguente codice crea MainActivity come attività di avvio e carica la libreria nativa principale dell'applicazione, 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. Crea un tema dell'app a schermo intero nel file 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. Applica il tema all'applicazione nel file AndroidManifest.xml:

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

    Per istruzioni dettagliate sulla modalità a schermo intero, consulta la guida immersiva e l'implementazione di esempio nel repository giochi-esempi.

Questa guida alla migrazione non modifica il nome della libreria nativa. Se decidi di modificarlo, assicurati che i nomi delle librerie native siano coerenti nelle tre posizioni seguenti:

  • Codice Kotlin o Java:

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

    <meta-data android:name="android.app.lib_name"
            android:value="AndroidGame" />
    
  • All'interno del file di script di build C/C++, ad esempio CMakeLists.txt:

    add_library(AndroidGame ...)
    

Aggiornamenti degli script di build C/C++

Le istruzioni in questa sezione utilizzano cmake come esempio. Se la tua applicazione usa ndk-build, devi mapparli ai comandi equivalenti descritti nella pagina della documentazione di ndk-build.

L'implementazione C/C++ di GameActivity ha fornito una release del codice sorgente. Per la versione 1.2.2 a successive, viene fornita una release statica della libreria. La libreria statica è il tipo di release consigliato.

La release è inclusa nell'AAR con l'utilità prefab. Il codice nativo include le origini C/C++ di GameActivity e il codice native_app_glue. Devono essere creati insieme al codice C/C++ dell'applicazione.

NativeActivity applicazioni utilizzano già il codice native_app_glue fornito con NDK. Devi sostituirlo con la versione di GameActivity di native_app_glue. A parte questo, si applicano tutti i cmake passaggi documentati nella guida introduttiva:

  • Importa la libreria statica C/C++ o il codice sorgente di C/++ nel progetto nel seguente modo.

    Libreria statica

    Nel file CMakeLists.txt del tuo progetto, importa la libreria statica game-activity nel modulo prefabbricato game-activity_static:

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

    Codice sorgente

    Nel file CMakeLists.txt del progetto, importa il pacchetto game-activity e aggiungilo alla destinazione. Il pacchetto game-activity richiede libandroid.so, quindi, se non è presente, devi importarlo.

    find_package(game-activity REQUIRED CONFIG)
    ...
    target_link_libraries(... android game-activity::game-activity)
    
  • Rimuovi tutti i riferimenti al codice native_app_glue di NDK, ad esempio:

    ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c
        ...
    set(CMAKE_SHARED_LINKER_FLAGS
        "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
    
  • Se utilizzi la release del codice sorgente, includi i file sorgente GameActivity. In caso contrario, salta questo passaggio.

    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)
    

Risolvere il problema UnfilledLinkError

Se riscontri un UnsatsifiedLinkError per la funzione com.google.androidgamesdk.GameActivity.initializeNativeCode(), aggiungi questo codice al file CMakeLists.txt:

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

Aggiornamenti del codice sorgente C/C++

Segui questi passaggi per sostituire i riferimenti NativeActivity nella tua applicazione con GameActivity:

  • Utilizza il native_app_glue rilasciato con GameActivity. Cerca e sostituisci tutti gli utilizzi di android_native_app_glue.h con:

    #include <game-activity/native_app_glue/android_native_app_glue.h>
    
  • Imposta il filtro per gli eventi di movimento e il filtro degli eventi chiave su NULL in modo che la tua app possa ricevere eventi di input da tutti i dispositivi di input. In genere questa operazione viene eseguita all'interno della funzione 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.
    }
    
  • Rimuovi il codice correlato di AInputEvent e sostituiscilo con l'implementazione InputBuffer di 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;
    }
    
  • Esamina e aggiorna la logica collegata a AInputEvent di NativeActivity. Come mostrato nel passaggio precedente, l'elaborazione di InputBuffer di GameActivity è al di fuori del loop ALooper_pollAll().

  • Sostituisci l'utilizzo di android_app::activity->clazz con android_app:: activity->javaGameActivity. GameActivity rinomina l'istanza GameActivity Java.

Passaggi aggiuntivi

I passaggi precedenti riguardano la funzionalità di NativeActivity, ma GameActivity ha funzionalità aggiuntive che potresti voler utilizzare:

Ti consigliamo di esplorare queste funzionalità e adottarle in base ai tuoi giochi.

Se hai domande o consigli per GameActivity o altre librerie AGDK, crea un bug per farcelo sapere.