Bermigrasi dari NativeActivity   Bagian dari Android Game Development Kit.

Halaman ini menjelaskan cara melakukan migrasi dari NativeActivity ke GameActivity dalam project game Android Anda.

GameActivity didasarkan pada NativeActivity dari framework Android, dengan peningkatan dan fitur baru:

  • Mendukung Fragment dari Jetpack.
  • Menambahkan dukungan TextInput untuk memfasilitasi integrasi keyboard virtual.
  • Menangani peristiwa sentuh dan tombol di class Java GameActivity, bukan antarmuka NativeActivity onInputEvent.

Sebelum bermigrasi, sebaiknya baca panduan memulai, yang menjelaskan cara menyiapkan dan mengintegrasikan GameActivity dalam project Anda.

Update skrip build Java

GameActivity didistribusikan sebagai library Jetpack. Pastikan untuk menerapkan langkah-langkah update skrip Gradle yang dijelaskan dalam panduan memulai:

  1. Aktifkan library Jetpack di file gradle.properties project Anda:

    android.useAndroidX=true
    
  2. Secara opsional, tentukan versi Prefab dalam file gradle.properties yang sama, misalnya:

    android.prefabVersion=2.0.0
    
  3. Aktifkan fitur Prefab di file build.gradle aplikasi Anda:

    android {
        ... // other configurations
        buildFeatures.prefab true
    }
    
  4. Tambahkan dependensi GameActivity ke aplikasi Anda:

    1. Tambahkan library core dan games-activity.
    2. Jika API level minimum yang didukung saat ini kurang dari 16, update ke setidaknya 16.
    3. Update versi SDK yang telah dikompilasi ke versi yang diperlukan library games-activity. Biasanya Jetpack memerlukan versi SDK terbaru pada waktu build rilis.

    File build.gradle Anda yang telah diupdate mungkin terlihat seperti ini:

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

Update kode Kotlin atau Java

NativeActivity dapat digunakan sebagai aktivitas pengaktifan dan membuat aplikasi layar penuh. Saat ini, GameActivity tidak dapat digunakan sebagai aktivitas pengaktifan. Aplikasi harus mengambil class dari GameActivity dan menggunakannya sebagai aktivitas pengaktifan. Anda juga harus membuat perubahan konfigurasi tambahan untuk membuat aplikasi layar penuh.

Langkah-langkah berikut mengasumsikan bahwa aplikasi Anda menggunakan NativeActivity sebagai aktivitas pengaktifan. Jika tidak demikian, Anda dapat melewati sebagian besar langkah tersebut.

  1. Buat file Kotlin atau Java untuk menghosting aktivitas pengaktifan baru. Misalnya, kode berikut membuat MainActivity sebagai aktivitas pengaktifan dan memuat library native utama aplikasi, 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. Buat tema aplikasi layar penuh di 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. Terapkan tema ke aplikasi di file AndroidManifest.xml:

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

    Untuk petunjuk mendetail tentang mode layar penuh, lihat panduan imersif dan contoh implementasi di repo contoh game.

Panduan migrasi ini tidak mengubah nama library native. Jika Anda mengubahnya, pastikan nama library native konsisten di tiga lokasi berikut:

  • Kode Kotlin atau Java:

    System.loadLibrary(AndroidGame)
    
  • AndroidManifest.xml:

    <meta-data android:name="android.app.lib_name"
            android:value="AndroidGame" />
    
  • Di dalam file skrip build C/C++, misalnya CMakeLists.txt:

    add_library(AndroidGame ...)
    

Update skrip build C/C++

Petunjuk di bagian ini menggunakan cmake sebagai contoh. Jika aplikasi Anda menggunakan ndk-build, Anda perlu memetakannya ke perintah yang setara yang dijelaskan di halaman dokumentasi ndk-build.

Implementasi C/C++ GameActivity telah menyediakan rilis kode sumber. Untuk versi 1.2.2 yang lebih baru, rilis library statis disediakan. Library statis adalah jenis rilis yang direkomendasikan.

Rilis dikemas dalam AAR dengan utilitas prefab. Kode native mencakup sumber C/C++ GameActivity dan kode native_app_glue. Keduanya harus di-build bersama dengan kode C/C++ aplikasi Anda.

Aplikasi NativeActivity sudah menggunakan kode native_app_glue yang dikirimkan di dalam NDK. Anda harus menggantinya dengan native_app_glue versi GameActivity. Selain itu, semua langkah cmake yang didokumentasikan dalam panduan memulai tetap berlaku:

  • Impor library statis C/C++ atau kode sumber C/++ ke project Anda sebagai berikut.

    Library statis

    Dalam file CMakeLists.txt project Anda, impor library statis game-activity ke dalam modul prefab game-activity_static:

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

    Kode sumber

    Dalam file CMakeLists.txt project Anda, impor paket game-activity lalu tambahkan ke target Anda. Paket game-activity memerlukan libandroid.so, jadi jika tidak ada, Anda juga harus mengimpornya.

    find_package(game-activity REQUIRED CONFIG)
    ...
    target_link_libraries(... android game-activity::game-activity)
    
  • Hapus semua referensi ke kode native_app_glue NDK, seperti:

    ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c
        ...
    set(CMAKE_SHARED_LINKER_FLAGS
        "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
    
  • Jika Anda menggunakan rilis kode sumber, sertakan file sumber GameActivity. Jika tidak, lewati langkah ini.

    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)
    

Mengatasi masalah UnsatisfiedLinkError

Jika Anda menemukan UnsatsifiedLinkError untuk fungsi com.google.androidgamesdk.GameActivity.initializeNativeCode(), tambahkan kode ini ke file CMakeLists.txt:

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

Update kode sumber C/C++

Ikuti langkah-langkah berikut untuk mengganti referensi NativeActivity di aplikasi Anda dengan GameActivity:

  • Gunakan native_app_glue yang dirilis dengan GameActivity. Telusuri dan ganti semua penggunaan android_native_app_glue.h dengan:

    #include <game-activity/native_app_glue/android_native_app_glue.h>
    
  • Tetapkan filter peristiwa gerakan dan filter peristiwa tombol ke NULL agar aplikasi Anda dapat menerima peristiwa input dari semua perangkat input. Anda biasanya dapat melakukannya dalam fungsi 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.
    }
    
  • Hapus kode terkait AInputEvent, lalu ganti dengan implementasi InputBuffer 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;
    }
    
  • Tinjau dan update logika yang dilampirkan ke AInputEvent NativeActivity. Seperti yang ditunjukkan pada langkah sebelumnya, pemrosesan InputBuffer GameActivity berada di luar loop ALooper_pollAll().

  • Ganti penggunaan android_app::activity->clazz dengan android_app:: activity->javaGameActivity. GameActivity mengganti nama instance GameActivity Java.

Langkah-langkah tambahan

Langkah-langkah sebelumnya mencakup fungsi NativeActivity, tetapi GameActivity memiliki fitur tambahan yang dapat Anda gunakan:

Sebaiknya jelajahi fitur ini dan gunakan fitur yang sesuai untuk game Anda.

Jika ada pertanyaan atau rekomendasi mengenai GameActivity atau library AGDK lainnya, buat bug untuk memberi tahu kami.