Миграция из NativeActivity Часть Android Game Development Kit .
На этой странице описывается, как выполнить миграцию из NativeActivity
в GameActivity
в вашем игровом проекте Android.
GameActivity
основан на NativeActivity
из фреймворка Android с улучшениями и новыми функциями:
- Поддерживает
Fragment
от Jetpack. - Добавляет поддержку
TextInput
для облегчения интеграции виртуальной клавиатуры. - Обрабатывает события прикосновения и нажатия клавиш в классе Java
GameActivity
, а не в интерфейсеNativeActivity
onInputEvent
.
Перед миграцией рекомендуем прочитать руководство по началу работы , в котором описывается, как настроить и интегрировать GameActivity
в ваш проект.
Обновления скриптов сборки Java
GameActivity
распространяется как библиотека Jetpack. Обязательно выполните шаги по обновлению скрипта Gradle, описанные в руководстве по началу работы :
Включите библиотеку Jetpack в файле
gradle.properties
вашего проекта:android.useAndroidX=true
При желании укажите версию Prefab в том же файле
gradle.properties
, например:android.prefabVersion=2.0.0
Включите функцию Prefab в файле
build.gradle
вашего приложения:android { ... // other configurations buildFeatures.prefab true }
Добавьте зависимость
GameActivity
к вашему приложению:- Добавьте библиотеки
core
иgames-activity
. - Если ваш текущий минимальный поддерживаемый уровень API меньше 16, обновите его как минимум до 16.
- Обновите скомпилированную версию 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
в качестве стартовой активности. Если это не так, вы можете пропустить большинство шагов.
Создайте файл 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"); } }
Создайте тему полноэкранного приложения в файле
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>
Примените тему к приложению в файле
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
в модуль prefabgame-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 переименовывает экземпляр JavaGameActivity
.
Дополнительные шаги
Предыдущие шаги охватывают функциональность NativeActivity, но у GameActivity
есть дополнительные функции, которые вы, возможно, захотите использовать:
- Ввод текста .
- Игровой контроллер .
- Фрагмент .
- Новые команды InSets окна, определенные в NativeAppGlueAppCmd .
Мы рекомендуем изучить эти функции и адаптировать их под свои игры.
Если у вас есть вопросы или рекомендации по GameActivity или другим библиотекам AGDK, создайте баг-сообщение , чтобы сообщить нам.