Premiers pas avec GameActivity Composant d'Android Game Development Kit.
Ce guide explique comment configurer et intégrer GameActivity
, et comment gérer les événements dans votre jeu Android.
GameActivity
vous aide à transférer votre jeu C ou C++ sous Android en simplifiant le processus d'utilisation des API essentielles.
Auparavant, NativeActivity
était la classe recommandée pour les jeux. GameActivity
est désormais recommandée à sa place ; cette classe est rétrocompatible jusqu'au niveau 19 de l'API.
Pour un exemple intégrant GameActivity, accédez au dépôt games-samples.
Avant de commencer
Consultez les versions de GameActivity
pour obtenir une distribution.
Configurer votre build
Sous Android, une Activity
sert de point d'entrée à votre jeu et fournit la Window
dans laquelle vous pouvez dessiner. De nombreux jeux étendent cette Activity
avec leur propre classe Java ou Kotlin afin de contourner les limitations de NativeActivity
tout en utilisant le code JNI
pour faire le lien avec leur code de jeu C ou C++.
GameActivity
offre les fonctionnalités suivantes :
Hérite de
AppCompatActivity
, ce qui vous permet d'utiliser les composants d'architecture Android Jetpack.S'affiche dans une
SurfaceView
qui vous permet d'interagir avec tout autre élément d'interface utilisateur Android.Gère les événements d'activité Java. Cela permet d'intégrer n'importe quel élément d'interface utilisateur Android (tel que
EditText
,WebView
ouAd
) à votre jeu via une interface C.Offre une API C semblable à
NativeActivity
, et la bibliothèqueandroid_native_app_glue
.
GameActivity
est distribué sous forme d'archive Android (AAR). Cette AAR contient la classe Java que vous utilisez dans votre fichier AndroidManifest.xml
, ainsi que le code source C et C++ qui connecte le côté Java de GameActivity
à l'implémentation C/C++ de l'application. Si vous utilisez GameActivity
1.2.2 ou une version ultérieure, la bibliothèque statique C/C++ est également fournie. Le cas échéant, nous vous recommandons d'utiliser la bibliothèque statique plutôt que le code source.
Incluez ces fichiers sources ou la bibliothèque statique dans votre processus de compilation via Prefab
, qui expose les bibliothèques natives et le code source au niveau de votre projet CMake ou de votre build NDK.
Suivez les instructions de la page Jetpack Jeux Android pour ajouter la dépendance de la bibliothèque
GameActivity
au fichierbuild.gradle
de votre jeu.Activez Prefab en procédant comme suit avec le plug-in Android (AGP) version 4.1 ou ultérieure :
- Ajoutez le code suivant au bloc
android
du fichierbuild.gradle
de votre module :
buildFeatures { prefab true }
- Sélectionnez une version de Prefab et définissez-la dans le fichier
gradle.properties
:
android.prefabVersion=2.0.0
Si vous utilisez des versions antérieures du plug-in AGP, suivez la documentation de Prefab pour obtenir les instructions de configuration correspondantes.
- Ajoutez le code suivant au bloc
Importez la bibliothèque statique C/C++ ou le code source C/++ dans votre projet comme suit.
Bibliothèque statique
Dans le fichier
CMakeLists.txt
de votre projet, importez la bibliothèque statiquegame-activity
dans le module Prefabgame-activity_static
:find_package(game-activity REQUIRED CONFIG) target_link_libraries(${PROJECT_NAME} PUBLIC log android game-activity::game-activity_static)
Code source
Dans le fichier
CMakeLists.txt
de votre projet, importez le packagegame-activity
et ajoutez-le à votre cible. Le packagegame-activity
nécessitelibandroid.so
. S'il est absent, vous devez donc également l'importer.find_package(game-activity REQUIRED CONFIG) ... target_link_libraries(... android game-activity::game-activity)
Incluez également les fichiers suivants dans le fichier
CmakeLists.txt
de votre projet :GameActivity.cpp
,GameTextInput.cpp
etandroid_native_app_glue.c
.
Comment Android lance-t-il votre instance Activity ?
Le système Android exécute le code dans votre instance Activity en appelant des méthodes de rappel qui correspondent à des étapes spécifiques du cycle de vie de l'activité. Pour qu'Android puisse lancer votre activité et démarrer votre jeu, vous devez déclarer l'activité avec les attributs appropriés dans le fichier manifeste Android. Pour en savoir plus, consultez Présentation des activités.
Fichier manifeste Android
Chaque projet d'application doit disposer d'un fichier AndroidManifest.xml à la racine de l'ensemble de sources du projet. Le fichier manifeste fournit aux outils de compilation Android, au système d'exploitation Android et à Google Play des informations essentielles sur votre application. Par exemple :
Nom du package et ID de l'application pour identifier de façon unique votre jeu sur Google Play
Composants d'application tels que les activités, les services, les broadcast receivers et les fournisseurs de contenu
Autorisations d'accès à des parties protégées du système ou à d'autres applis
Compatibilité des appareils afin de spécifier les exigences matérielles et logicielles de votre jeu
Nom de la bibliothèque native pour
GameActivity
etNativeActivity
(par défaut, libmain.so)
Implémenter GameActivity dans votre jeu
Créez ou identifiez la classe Java de votre activité principale (celle spécifiée dans l'élément
activity
de votre fichierAndroidManifest.xml
). Modifiez cette classe pour étendreGameActivity
à partir du packagecom.google.androidgamesdk
:import com.google.androidgamesdk.GameActivity; public class YourGameActivity extends GameActivity { ... }
Assurez-vous que votre bibliothèque native est chargée au démarrage à l'aide d'un bloc statique :
public class EndlessTunnelActivity extends GameActivity { static { // Load the native library. // The name "android-game" depends on your CMake configuration, must be // consistent here and inside AndroidManifect.xml System.loadLibrary("android-game"); } ... }
Ajoutez votre bibliothèque native au fichier
AndroidManifest.xml
si le nom de votre bibliothèque n'est pas le nom par défaut (libmain.so
) :<meta-data android:name="android.app.lib_name" android:value="android-game" />
Implémenter android_main
La bibliothèque
android_native_app_glue
est une bibliothèque de code source que votre jeu utilise pour gérer les événements de cycle de vieGameActivity
dans un thread distinct afin d'éviter le blocage de votre thread principal. Lorsque vous utilisez la bibliothèque, vous enregistrez le rappel pour gérer les événements de cycle de vie, tels que la saisie tactile événements. L'archiveGameActivity
comprend sa propre version de la bibliothèqueandroid_native_app_glue
. Vous ne pouvez donc pas utiliser la version incluse dans les versions du NDK. Si vos jeux utilisent la bibliothèqueandroid_native_app_glue
incluse dans le NDK, passez à la versionGameActivity
.Une fois que vous avez ajouté le code source de la bibliothèque
android_native_app_glue
à votre projet, elle interagit avecGameActivity
. Implémentez une fonction appeléeandroid_main
, qui est appelée par la bibliothèque et utilisée comme point d'entrée de votre jeu. Elle est transmise à une structure appeléeandroid_app
. Cette structure peut varier selon votre jeu et votre moteur. Exemple :#include <game-activity/native_app_glue/android_native_app_glue.h> extern "C" { void android_main(struct android_app* state); }; void android_main(struct android_app* app) { NativeEngine *engine = new NativeEngine(app); engine->GameLoop(); delete engine; }
Traitez
android_app
dans votre boucle de jeu principale, par exemple pour interroger et gérer les événements de cycle de vie d'application définis dans NativeAppGlueAppCmd. L'extrait de code suivant enregistre la fonction_hand_cmd_proxy
en tant que gestionnaireNativeAppGlueAppCmd
, puis interroge les événements de cycle de vie d'application et les envoie au gestionnaire enregistré (dansandroid_app::onAppCmd
) pour traitement :void NativeEngine::GameLoop() { mApp->userData = this; mApp->onAppCmd = _handle_cmd_proxy; // register your command handler. mApp->textInputState = 0; while (1) { int events; struct android_poll_source* source; // If not animating, block until we get an event; // If animating, don't block. while ((ALooper_pollAll(IsAnimating() ? 0 : -1, NULL, &events, (void **) &source)) >= 0) { if (source != NULL) { // process events, native_app_glue internally sends the outstanding // application lifecycle events to mApp->onAppCmd. source->process(source->app, source); } if (mApp->destroyRequested) { return; } } if (IsAnimating()) { DoFrame(); } } }
Pour en savoir plus, étudiez l'implémentation de Endless Tunnel. Exemple de NDK La principale différence réside dans la façon dont les événements sont gérés, comme indiqué dans la section suivante.
Gérer les événements
Pour que les événements d'entrée puissent accéder à votre application, créez et enregistrez vos filtres d'événements avec android_app_set_motion_event_filter
et android_app_set_key_event_filter
.
Par défaut, la bibliothèque native_app_glue
n'autorise que les événements de mouvement provenant de l'entrée SOURCE_TOUCHSCREEN. Pour en savoir plus, consultez la documentation de référence et le code d'implémentation d'android_native_app_glue
.
Pour gérer les événements d'entrée, obtenez une référence à android_input_buffer
avec android_app_swap_input_buffers()
dans votre boucle de jeu. Ceux-ci contiennent les événements de mouvement et les événements clés qui se sont produits depuis la dernière interrogation. Le nombre d'événements contenus est respectivement stocké dans motionEventsCount
et keyEventsCount
.
Itérez et gérez chacun des événements de votre boucle de jeu. Dans cet exemple, le code suivant itère les
motionEvents
et les gère viahandle_event
:android_input_buffer* inputBuffer = android_app_swap_input_buffers(app); if (inputBuffer && inputBuffer->motionEventsCount) { for (uint64_t i = 0; i < inputBuffer->motionEventsCount; ++i) { GameActivityMotionEvent* motionEvent = &inputBuffer->motionEvents[i]; if (motionEvent->pointerCount > 0) { const int action = motionEvent->action; const int actionMasked = action & AMOTION_EVENT_ACTION_MASK; // Initialize pointerIndex to the max size, we only cook an // event at the end of the function if pointerIndex is set to a valid index range uint32_t pointerIndex = GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT; struct CookedEvent ev; memset(&ev, 0, sizeof(ev)); ev.motionIsOnScreen = motionEvent->source == AINPUT_SOURCE_TOUCHSCREEN; if (ev.motionIsOnScreen) { // use screen size as the motion range ev.motionMinX = 0.0f; ev.motionMaxX = SceneManager::GetInstance()->GetScreenWidth(); ev.motionMinY = 0.0f; ev.motionMaxY = SceneManager::GetInstance()->GetScreenHeight(); } switch (actionMasked) { case AMOTION_EVENT_ACTION_DOWN: pointerIndex = 0; ev.type = COOKED_EVENT_TYPE_POINTER_DOWN; break; case AMOTION_EVENT_ACTION_POINTER_DOWN: pointerIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); ev.type = COOKED_EVENT_TYPE_POINTER_DOWN; break; case AMOTION_EVENT_ACTION_UP: pointerIndex = 0; ev.type = COOKED_EVENT_TYPE_POINTER_UP; break; case AMOTION_EVENT_ACTION_POINTER_UP: pointerIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); ev.type = COOKED_EVENT_TYPE_POINTER_UP; break; case AMOTION_EVENT_ACTION_MOVE: { // Move includes all active pointers, so loop and process them here, // we do not set pointerIndex since we are cooking the events in // this loop rather than at the bottom of the function ev.type = COOKED_EVENT_TYPE_POINTER_MOVE; for (uint32_t i = 0; i < motionEvent->pointerCount; ++i) { _cookEventForPointerIndex(motionEvent, callback, ev, i); } break; } default: break; } // Only cook an event if we set the pointerIndex to a valid range, note that // move events cook above in the switch statement. if (pointerIndex != GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT) { _cookEventForPointerIndex(motionEvent, callback, ev, pointerIndex); } } } android_app_clear_motion_events(inputBuffer); }
Consultez le Exemple GitHub pour l'implémentation de
_cookEventForPointerIndex()
et d'autres fonctions associées.Lorsque vous avez terminé, n'oubliez pas de vider la file d'attente des événements que vous venez de gérer :
android_app_clear_motion_events(mApp);
Ressources supplémentaires
Pour en savoir plus sur GameActivity
, consultez :
- Notes de version de GameActivity et d'AGDK
- Utiliser GameTextInput dans GameActivity
- Guide de migration de NativeActivity
- Documentation de référence sur GameActivity
- Implémentation de GameActivity
Pour signaler des bugs ou demander de nouvelles fonctionnalités dans GameActivity, utilisez l'outil Issue Tracker de GameActivity.