Migrar da NativeActivity Parte do Android Game Development Kit.
Esta página descreve como migrar de
NativeActivity
para
GameActivity
no seu projeto de jogo Android.
A GameActivity
tem como base a NativeActivity
do framework do
Android, com melhorias e novos recursos:
- Oferece suporte ao
Fragment
do Jetpack. - Oferece suporte à
TextInput
, para facilitar a integração do teclado de software. - Processa eventos de toque e pressionamentos de tecla na classe Java
GameActivity
, e não na da interfaceonInputEvent
daNativeActivity
.
Antes de migrar, recomendamos a leitura do
guia de iniciação, que descreve como
configurar e integrar a GameActivity
ao projeto.
Atualizações de scripts de build Java
GameActivity
é distribuída como uma
biblioteca do Jetpack. Você precisa seguir as etapas de atualização do script do Gradle
apresentadas no guia de introdução:
Ative a biblioteca Jetpack no arquivo
gradle.properties
do projeto:android.useAndroidX=true
Também é possível especificar uma versão do Prefab no mesmo arquivo
gradle.properties
, por exemplo:android.prefabVersion=2.0.0
Ative o recurso Prefab no arquivo
build.gradle
do app:android { ... // other configurations buildFeatures.prefab true }
Adicione a dependência
GameActivity
ao app:- Adicione as bibliotecas
core
egames-activity
. - Se o nível mínimo da API com suporte for inferior ao 16, atualize-o para o 16 ou mais recente.
- Atualize a versão compilada do SDK para a versão exigida pela
biblioteca
games-activity
. O Jetpack normalmente exige a versão mais recente do SDK no momento de lançamento do build.
O arquivo
build.gradle
atualizado vai ficar assim: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' }
- Adicione as bibliotecas
Atualizações de código Kotlin ou Java
A NativeActivity
pode ser usada como uma atividade de inicialização, que abre um app
em tela cheia. No momento, a GameActivity não pode ser usada como a atividade
de inicialização. Os apps precisam derivar uma classe de GameActivity
e usá-la para a
inicialização. Também é necessário fazer outras mudanças de configuração para
criar um app que vai ser mostrado em tela cheia.
Nas etapas abaixo, presumimos que o app usa NativeActivity
como a atividade
de inicialização. Se esse não for o caso, você pode pular a maioria dessas etapas.
Crie um arquivo Kotlin ou Java para hospedar a nova atividade de inicialização. Por exemplo, o código abaixo cria a
MainActivity
como atividade de inicialização e carrega a biblioteca nativa principal do app,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"); } }
Crie um tema de app em tela cheia no arquivo
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>
Aplique esse tema ao app no arquivo
AndroidManifest.xml
:<application android:theme=”@style/Application.Fullscreen”> <!-- other configurations not listed here. --> </application>
Para ver instruções detalhadas sobre o modo de tela cheia, consulte o guia de imersão e confira um exemplo de implementação no repositório games-samples (em inglês).
Nas etapas apresentadas neste guia de migração, não mudamos o nome da biblioteca nativa. Caso você faça essa mudança, confirme se os nomes das bibliotecas nativas estão consistentes nestes três locais:
Código Kotlin ou Java:
System.loadLibrary(“AndroidGame”)
AndroidManifest.xml
:<meta-data android:name="android.app.lib_name" android:value="AndroidGame" />
No arquivo do script de build C/C++, por exemplo,
CMakeLists.txt
:add_library(AndroidGame ...)
Atualizações de scripts de build C/C++
As instruções nesta seção usam cmake
como exemplo. Caso seu app
use o ndk-build
, considere os comandos equivalentes descritos na
página de documentação do ndk-build.
A implementação C/C++ da GameActivity oferece uma versão do código-fonte. Para a versão 1.2.2 e mais recentes, é fornecida uma versão de biblioteca estática. A biblioteca estática é o tipo de versão recomendado.
A versão é incluída no AAR com o
utilitário
prefab
. O código nativo inclui as origens C/C++ da GameActivity e o
código native_app_glue
. Eles precisam ser criados junto ao
código C/C++ do app.
Apps com a NativeActivity
já usam o código native_app_glue
incluído no NDK. No entanto, é necessário substituir esse elemento pela versão
de native_app_glue
da GameActivity. Além disso, todas as etapas cmake
documentadas no
guia de introdução também são válidas:
Importe a biblioteca estática C/C++ ou o código-fonte C/++ para seu projeto da seguinte maneira:
Biblioteca estática
No arquivo
CMakeLists.txt
do projeto, importe a biblioteca estáticagame-activity
para o 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-fonte
No arquivo
CMakeLists.txt
do projeto, importe o pacotegame-activity
e adicione-o ao destino: O pacotegame-activity
requerlibandroid.so
. Portanto, se ele estiver ausente, será necessário importá-lo.find_package(game-activity REQUIRED CONFIG) ... target_link_libraries(... android game-activity::game-activity)
Remova todas as referências ao código
native_app_glue
do NDK, como:${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ... set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
Se você estiver usando a versão do código-fonte, inclua os arquivos de origem
GameActivity
. Caso contrário, pule esta etapa.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)
Resolver o problema UnsatisfiedLinkError
Se você encontrar um UnsatsifiedLinkError
para a
função com.google.androidgamesdk.GameActivity.initializeNativeCode()
, adicione
este código ao arquivo CMakeLists.txt
:
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -u \
Java_com_google_androidgamesdk_GameActivity_initializeNativeCode")
Atualizações de código-fonte C/C++
Siga estas etapas para substituir referências NativeActivity
no
app por GameActivity
:
Use o elemento
native_app_glue
lançado comGameActivity
. Pesquise e substitua todas as ocorrências deandroid_native_app_glue.h
por:#include <game-activity/native_app_glue/android_native_app_glue.h>
Defina o filtro dos eventos de movimento e dos de tecla como
NULL
para que seu app possa receber eventos de todos os dispositivos de entrada. Normalmente, isso é feito dentro da funçãoandroid_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. }
Remova o código
AInputEvent
relacionado e o substitua pela implementação deInputBuffer
da 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; }
Revise e atualize a lógica ligada ao
AInputEvent
da NativeActivity. Como mostrado na etapa anterior, o processamento deInputBuffer
da GameActivity ocorre fora do loopALooper_pollAll()
.Substitua o uso de
android_app::activity->clazz
porandroid_app:: activity->javaGameActivity
. A GameActivity renomeia a instância Java daGameActivity
.
Outras etapas
As etapas anteriores abrangem as funções da NativeActivity, mas a GameActivity
inclui
outros recursos que você pode usar:
- TextInput
- Game Controller
- Fragment
- Novos comandos InSets de janela definidos no NativeAppGlueAppCmd.
Recomendamos que você conheça esses recursos e os adote conforme necessário para seus jogos.
Caso você tenha dúvidas ou recomendações relacionadas à GameActivity ou outras bibliotecas do AGDK, crie um relatório de bug para nos contar sobre isso.