Переход с NativeActivity Входит в Android Game Development Kit.
На этой странице описано, как перейти с NativeActivity на GameActivity в проекте игры для Android.
Класс GameActivity основан на классе NativeActivity из фреймворка Android, но имеет ряд улучшений и новых функций:
- Поддерживает
Fragmentиз Jetpack. - Добавлена поддержка
TextInputдля упрощения интеграции экранной клавиатуры. - Обрабатывает события касания и нажатия клавиш в классе Java
GameActivity, а не в интерфейсеNativeActivityonInputEvent.
Перед переносом рекомендуем ознакомиться с руководством по началу работы, в котором описано, как настроить и интегрировать 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: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"); } }
Создайте тему приложения для полноэкранного режима в файле
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/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_glueNDK, например:${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; }Проверьте и обновите логику, связанную с
AInputEventNativeActivity. Как показано на предыдущем шаге, обработкаInputBufferв GameActivity выполняется за пределами циклаALooper_pollOnce().Замените
android_app::activity->clazzнаandroid_app:: activity->javaGameActivity. GameActivity переименовывает экземпляр JavaGameActivity.
Дополнительные действия
В предыдущих шагах описаны функции NativeActivity, но у GameActivity есть дополнительные возможности, которые могут вам пригодиться:
- TextInput.
- Игровой контроллер.
- Fragment.
- Новые команды InSets для окон, определенные в NativeAppGlueAppCmd.
Рекомендуем изучить эти функции и использовать их в своих играх.
Если у вас есть вопросы или рекомендации по GameActivity или другим библиотекам AGDK, сообщите нам об этом, создав ошибку.