Миграция из NativeActivity Часть Android Game Development Kit .

На этой странице описывается, как выполнить миграцию из NativeActivity в GameActivity в вашем игровом проекте Android.

GameActivity основан на NativeActivity из фреймворка Android с улучшениями и новыми функциями:

  • Поддерживает Fragment от Jetpack.
  • Добавляет поддержку TextInput для облегчения интеграции виртуальной клавиатуры.
  • Обрабатывает события прикосновения и нажатия клавиш в классе Java GameActivity , а не в интерфейсе NativeActivity onInputEvent .

Перед миграцией рекомендуем прочитать руководство по началу работы , в котором описывается, как настроить и интегрировать GameActivity в ваш проект.

Обновления скриптов сборки Java

GameActivity распространяется как библиотека Jetpack. Обязательно выполните шаги по обновлению скрипта Gradle, описанные в руководстве по началу работы :

  1. Включите библиотеку Jetpack в файле gradle.properties вашего проекта:

    android.useAndroidX=true
    
  2. При желании укажите версию Prefab в том же файле gradle.properties , например:

    android.prefabVersion=2.0.0
    
  3. Включите функцию Prefab в файле build.gradle вашего приложения:

    android {
        ... // other configurations
        buildFeatures.prefab true
    }
    
  4. Добавьте зависимость GameActivity к вашему приложению:

    1. Добавьте библиотеки core и games-activity .
    2. Если ваш текущий минимальный поддерживаемый уровень API меньше 16, обновите его как минимум до 16.
    3. Обновите скомпилированную версию SDK до той, которая требуется для библиотеки games-activity . Jetpack обычно требует последнюю версию SDK на момент релиза.

    Ваш обновленный файл build.gradle может выглядеть примерно так:

    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 или Java

NativeActivity можно использовать в качестве стартовой активности и создать полноэкранное приложение. В настоящее время GameActivity не может использоваться в качестве стартовой активности. Приложения должны наследовать класс от GameActivity и использовать его в качестве стартовой активности. Для создания полноэкранного приложения также необходимо внести дополнительные изменения в конфигурацию.

В следующих шагах предполагается, что ваше приложение использует NativeActivity в качестве стартовой активности. Если это не так, вы можете пропустить большинство шагов.

  1. Создайте файл Kotlin или Java для размещения нового стартового действия. Например, следующий код создаёт MainActivity как стартовое действие и загружает основную нативную библиотеку приложения, libAndroidGame.so :

    Котлин

    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")
           }
       }
    }

    Ява

      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. Создайте тему полноэкранного приложения в файле 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. Примените тему к приложению в файле AndroidManifest.xml :

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

    Подробные инструкции по полноэкранному режиму см. в иммерсивном руководстве и примере реализации в репозитории games-samples .

Это руководство по миграции не меняет название нативной библиотеки. Если вы всё же решите его изменить, убедитесь, что названия нативных библиотек совпадают в следующих трёх местах:

  • Код Kotlin или Java:

    System.loadLibrary(AndroidGame)
    
  • AndroidManifest.xml :

    <meta-data android:name="android.app.lib_name"
            android:value="AndroidGame" />
    
  • Внутри файла скрипта сборки C/C++, например CMakeLists.txt :

    add_library(AndroidGame ...)
    

Обновления скриптов сборки C/C++

Инструкции в этом разделе используют cmake в качестве примера. Если ваше приложение использует ndk-build , вам необходимо сопоставить их с эквивалентными командами, описанными на странице документации ndk-build .

Реализация GameActivity на C/C++ доступна в виде исходного кода. Для версии 1.2.2 и более поздних версий доступна версия статической библиотеки. Рекомендуемый тип выпуска — статическая библиотека.

Релиз упакован в AAR-архив с утилитой prefab . Нативный код включает исходные коды GameActivity на C/C++ и код native_app_glue . Их необходимо скомпилировать вместе с кодом вашего приложения на C/C++.

Приложения NativeActivity уже используют код native_app_glue , поставляемый в составе NDK. Вам необходимо заменить его версией native_app_glue из GameActivity. В остальном применимы все шаги cmake , описанные в руководстве по началу работы:

  • Импортируйте статическую библиотеку C/C++ или исходный код C/++ в свой проект следующим образом.

    Статическая библиотека

    В файле CMakeLists.txt вашего проекта импортируйте статическую библиотеку game-activity в модуль prefab game-activity_static :

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

    Исходный код

    В файле CMakeLists.txt вашего проекта импортируйте пакет game-activity и добавьте его в целевой проект. Пакет game-activity требует libandroid.so , поэтому, если он отсутствует, его также необходимо импортировать.

    find_package(game-activity REQUIRED CONFIG)
    ...
    target_link_libraries(... android game-activity::game-activity)
    
  • Удалите все ссылки на код native_app_glue NDK, например:

    ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c
        ...
    set(CMAKE_SHARED_LINKER_FLAGS
        "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
    
  • Если вы используете исходный код, включите исходные файлы GameActivity . В противном случае пропустите этот шаг.

    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)
    

Обход проблемы UnsatisfiedLinkError

Если вы столкнулись с UnsatsifiedLinkError для функции com.google.androidgamesdk.GameActivity.initializeNativeCode() , добавьте этот код в файл CMakeLists.txt :

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

Обновления исходного кода C/C++

Выполните следующие шаги, чтобы заменить ссылки NativeActivity в вашем приложении на GameActivity :

  • Используйте native_app_glue , выпущенный вместе с GameActivity . Найдите и замените все случаи использования android_native_app_glue.h на:

    #include <game-activity/native_app_glue/android_native_app_glue.h>
    
  • Установите фильтры событий движения и нажатия клавиш в NULL , чтобы ваше приложение могло получать события ввода со всех устройств. Обычно это делается внутри функции 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.
    }
    
  • Удалите код, связанный с AInputEvent , и замените его реализацией 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_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;
    }
    
  • Проверьте и обновите логику, связанную с AInputEvent объекта NativeActivity. Как показано на предыдущем шаге, обработка InputBuffer объекта GameActivity осуществляется вне цикла ALooper_pollOnce() .

  • Замените использование android_app::activity->clazz на android_app:: activity->javaGameActivity . GameActivity переименовывает экземпляр Java GameActivity .

Дополнительные шаги

Предыдущие шаги охватывают функциональность NativeActivity, но у GameActivity есть дополнительные функции, которые вы, возможно, захотите использовать:

Мы рекомендуем изучить эти функции и адаптировать их под свои игры.

Если у вас есть вопросы или рекомендации по GameActivity или другим библиотекам AGDK, создайте баг-сообщение , чтобы сообщить нам.