Миграция с NativeActivity . Часть комплекта разработки игр для Android .
На этой странице описано, как перейти с 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 .
Это руководство по миграции не меняет имя собственной библиотеки. Если вы все же измените его, убедитесь, что имена собственных библиотек согласованы в следующих трех местах:
Котлин или 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
утилиты. Собственный код включает исходные коды C/C++ GameActivity и код 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)
Решение проблемы 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_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;
}Просмотрите и обновите логику, прикрепленную к
AInputEvent
NativeActivity. Как показано на предыдущем шаге, обработкаInputBuffer
GameActivity находится вне циклаALooper_pollAll()
.Замените использование
android_app::activity->clazz
наandroid_app:: activity->javaGameActivity
. GameActivity переименовывает экземпляр JavaGameActivity
.
Дополнительные шаги
Предыдущие шаги охватывают функциональность NativeActivity, но у GameActivity
есть дополнительные функции, которые вы, возможно, захотите использовать:
- Текстовый ввод .
- Игровой контроллер .
- Фрагмент .
- Команды InSets нового окна, определенные в NativeAppGlueAppCmd .
Мы рекомендуем изучить эти функции и использовать их в своих играх.
Если у вас есть какие-либо вопросы или рекомендации по GameActivity или другим библиотекам AGDK, создайте сообщение об ошибке и сообщите нам об этом.