Cómo migrar desde NativeActivity Parte de Android Game Development Kit.
En esta página, se describe cómo migrar de NativeActivity
a GameActivity
en tu proyecto de juego para Android.
GameActivity
se basa en NativeActivity
del framework de Android e incluye mejoras y funciones nuevas:
- Es compatible con
Fragment
de Jetpack. - Agrega compatibilidad con
TextInput
para facilitar la integración del teclado en pantalla. - Controla eventos táctiles y de teclas en la clase
GameActivity
de Java, en lugar de la interfazNativeActivity
onInputEvent
.
Antes de realizar la migración, te recomendamos que leas la guía de introducción, en la que se describe el proceso de configuración y la integración de GameActivity
en tu proyecto.
Actualizaciones de la secuencia de comandos de compilación de Java
GameActivity
se distribuye como una biblioteca de Jetpack. Asegúrate de aplicar los pasos de actualización de la secuencia de comandos de Gradle que se describen en la guía de introducción:
Habilita la biblioteca de Jetpack en el archivo
gradle.properties
de tu proyecto:android.useAndroidX=true
De manera opcional, especifica una versión de Prefab en el mismo archivo
gradle.properties
, por ejemplo:android.prefabVersion=2.0.0
Habilita la función de Prefab en el archivo
build.gradle
de tu app:android { ... // other configurations buildFeatures.prefab true }
Agrega la dependencia
GameActivity
a tu aplicación:- Agrega las bibliotecas
core
ygames-activity
. - Si tu nivel mínimo actual de API compatible es anterior a 16, actualízalo al menos al 16.
- Actualiza la versión del SDK compilada a la que requiere la biblioteca
games-activity
. Jetpack suele requerir la versión más reciente del SDK en el momento de compilación del lanzamiento.
El archivo
build.gradle
actualizado podría verse de la siguiente manera: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' }
- Agrega las bibliotecas
Actualizaciones de código Kotlin o Java
NativeActivity
se puede usar como una actividad de inicio y crear una aplicación de pantalla completa. En la actualidad, no se puede usar GameActivity como la actividad de inicio. Las apps deben derivar una clase de GameActivity
y usarla como actividad de inicio. También debes realizar cambios adicionales de configuración para crear una app de pantalla completa.
En los siguientes pasos, se da por sentado que tu aplicación usa NativeActivity
como la actividad de inicio. Si este no es el caso, puedes omitir la mayoría de ellos.
Crea un archivo de Kotlin o Java para alojar la actividad nueva de inicio. Por ejemplo, el siguiente código crea la
MainActivity
como la actividad de inicio y carga la biblioteca nativa principal de la aplicación,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"); } }
Crea un tema de app en pantalla completa en el archivo
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>
Aplica el tema a la aplicación en el archivo
AndroidManifest.xml
:<application android:theme=”@style/Application.Fullscreen”> <!-- other configurations not listed here. --> </application>
Para obtener instrucciones detalladas sobre el modo de pantalla completa, consulta la guía envolvente y la implementación de ejemplo en el repositorio de muestras de juegos.
Esta guía de migración no cambia el nombre de la biblioteca nativa. Si no lo cambias, asegúrate de que los nombres de las bibliotecas nativas sean consistentes en las siguientes tres ubicaciones:
Código Kotlin o Java:
System.loadLibrary(“AndroidGame”)
AndroidManifest.xml
:<meta-data android:name="android.app.lib_name" android:value="AndroidGame" />
Dentro del archivo de secuencia de comandos de compilación de C/C++, por ejemplo
CMakeLists.txt
:add_library(AndroidGame ...)
Actualizaciones de la secuencia de comandos de compilación de C/C++
En las instrucciones de esta sección, se usa cmake
como ejemplo. Si tu aplicación usa ndk-build
, debes asignarlas a los comandos equivalentes descritos en la página de documentación de ndk-build.
La implementación de C/C++ de GameActivity proporcionó una actualización del código fuente. A partir de la versión 1.2.2, se brinda una versión estática de la biblioteca. La biblioteca estática es el tipo de versión recomendado.
La versión está empaquetada dentro del AAR con la utilidad prefab
. El código nativo incluye las fuentes C/C++ de GameActivity y el código native_app_glue
. Deben compilarse junto con el código C/C++ de la aplicación.
Las aplicaciones NativeActivity
ya usan el código native_app_glue
enviado que se incluye en el NDK. Debes reemplazarlo por la versión de native_app_glue
de GameActivity. Aparte de esto, se aplican todos los pasos de cmake
documentados en la guía de introducción:
Importa la biblioteca estática de C/C++ o el código fuente de C/++ a tu proyecto de la siguiente manera.
Biblioteca estática
En el archivo
CMakeLists.txt
de tu proyecto, importa la biblioteca estáticagame-activity
al módulo prefabgame-activity_static
:find_package(game-activity REQUIRED CONFIG) target_link_libraries(${PROJECT_NAME} PUBLIC log android game-activity::game-activity_static)
Código fuente
En el archivo
CMakeLists.txt
de tu proyecto, importa el paquetegame-activity
y agrégalo a tu destino: El paquetegame-activity
requierelibandroid.so
, por lo que, si falta, también debes importarlo.find_package(game-activity REQUIRED CONFIG) ... target_link_libraries(... android game-activity::game-activity)
Quita todas las referencias al código
native_app_glue
del NDK, por ejemplo:${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ... set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
Si usas la versión del código fuente, incluye los archivos fuente
GameActivity
. De lo contrario, omite este paso.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)
Soluciona el problema de UnsatisfiedLinkError
Si encuentras un UnsatsifiedLinkError
para la función com.google.androidgamesdk.GameActivity.initializeNativeCode()
, agrega este código a tu archivo CMakeLists.txt
:
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -u \
Java_com_google_androidgamesdk_GameActivity_initializeNativeCode")
Actualizaciones del código fuente de C/C++
Sigue estos pasos para reemplazar las referencias de NativeActivity
en tu aplicación por GameActivity
:
Usa el
native_app_glue
lanzado conGameActivity
. Busca y reemplaza todo el uso deandroid_native_app_glue.h
por lo siguiente:#include <game-activity/native_app_glue/android_native_app_glue.h>
Configura el filtro de evento de movimiento y el filtro de evento de tecla en
NULL
para que tu app pueda recibir eventos de entrada de todos los dispositivos. Por lo general, debes hacerlo dentro de la funciónandroid_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. }
Quita el código relacionado con
AInputEvent
y reemplázalo por la implementación deInputBuffer
de 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; }
Revisa y actualiza la lógica que se adjuntó a
AInputEvent
de NativeActivity. Como se muestra en el paso anterior, el procesamiento deInputBuffer
de GameActivity se encuentra fuera del bucleALooper_pollAll()
.Reemplaza el uso de
android_app::activity->clazz
porandroid_app:: activity->javaGameActivity
. GameActivity cambia el nombre de la instanciaGameActivity
de Java.
Pasos adicionales
Los pasos anteriores abarcan las funciones de NativeActivity, pero GameActivity
tiene funciones adicionales que tal vez quieras usar:
- TextInput
- Control de juegos
- Fragment
- Nuevos comandos InSets de ventana definidos en NativeAppGlueAppCmd
Te recomendamos que explores estas funciones y las adoptes según corresponda para tus juegos.
Si tienes alguna pregunta o recomendación para GameActivity, o bien para otras bibliotecas de AGDK, crea un error para informarnos.