Переход с NativeActivity, входящей в состав Android Game Development Kit .
На этой странице описано, как перейти с NativeActivity на GameActivity в вашем проекте Android-игры.
GameActivity основана на NativeActivity из фреймворка Android, но с улучшениями и новыми функциями:
- Поддерживает
Fragmentиз Jetpack. - Добавлена поддержка
TextInputдля упрощения интеграции с экранной клавиатурой. - Обработка событий касания и нажатия клавиш осуществляется в Java-классе
GameActivity, а не через интерфейсonInputEventNativeActivity.
Перед миграцией рекомендуем ознакомиться с руководством по началу работы , в котором описывается, как настроить и интегрировать GameActivity в ваш проект.
обновления скриптов сборки Java
GameActivity распространяется как библиотека Jetpack. Обязательно выполните действия по обновлению скрипта Gradle, описанные в руководстве по началу работы :
Включите библиотеку Jetpack в файле
gradle.propertiesвашего проекта:android.useAndroidX=trueПри желании, в том же файле
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") } } }
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"); } }
Создайте тему оформления для полноэкранного приложения в файле
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в модуль префаба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)
Обход проблемы UnatisfiedLinkError
Если при вызове функции com.google.androidgamesdk.GameActivity.initializeNativeCode() возникает UnsatsifiedLinkError , добавьте следующий код в файл 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 обладает дополнительными возможностями, которые могут вам пригодиться:
- TextInput .
- Игровой контроллер .
- Фрагмент .
- Новые команды InSets для окон, определенные в NativeAppGlueAppCmd .
Мы рекомендуем изучить эти функции и адаптировать их под ваши игры.
Если у вас есть какие-либо вопросы или рекомендации по GameActivity или другим библиотекам AGDK, создайте сообщение об ошибке , чтобы сообщить нам об этом.