Mulai menggunakan GameActivity Bagian dari Android Game Development Kit.
Panduan ini menjelaskan cara menyiapkan dan mengintegrasikan
GameActivity serta menangani peristiwa di game
Android Anda.
GameActivity membantu Anda menghadirkan game C atau
C++ ke Android dengan menyederhanakan proses penggunaan API penting.
Sebelumnya, NativeActivity adalah
class yang direkomendasikan untuk game. GameActivity menggantikannya sebagai class yang direkomendasikan
untuk game dan memiliki kompatibilitas mundur hingga API level 19.
Untuk mengetahui contoh yang mengintegrasikan GameActivity, lihat repositori contoh game.
Sebelum memulai
Lihat rilis GameActivity untuk
mendapatkan distribusi.
Menyiapkan build
Di Android, Activity berfungsi sebagai titik
masuk untuk game Anda, dan juga menyediakan
Window untuk digambar di dalamnya. Banyak game memperluas
Activity ini dengan class Java atau Kotlin-nya sendiri untuk membatalkan batasan di
NativeActivity saat menggunakan kode JNI untuk dihubungkan
ke kode game C atau C++ mereka.
GameActivity menawarkan kemampuan berikut:
Mewarisi dari
AppCompatActivity, memungkinkan Anda menggunakan Komponen Arsitektur Android Jetpack.Merender ke dalam
SurfaceViewyang memungkinkan Anda untuk berinteraksi dengan elemen UI Android lainnya.Menangani peristiwa aktivitas Java. Hal ini memungkinkan elemen UI Android (seperti
EditText,WebView, atauAd) untuk diintegrasikan ke game Anda melalui antarmuka C.Menawarkan API C yang mirip dengan
NativeActivity, dan libraryandroid_native_app_glue.
GameActivity didistribusikan sebagai Android Archive
(AAR). AAR ini berisi class Java yang
Anda gunakan dalam
AndroidManifest.xml, serta kode sumber C
dan C++ yang menghubungkan sisi Java dari GameActivity ke implementasi
C/C++ aplikasi. Jika Anda menggunakan GameActivity 1.2.2 atau yang lebih baru, library statis
C/C++ juga akan disediakan. Jika ada, sebaiknya gunakan
library statis, bukan kode sumber.
Sertakan file sumber ini atau library statis sebagai bagian dari proses
build Anda melalui
Prefab,
yang mengekspos library native dan kode sumber ke
project CMake atau build NDK.
Ikuti petunjuk di halaman Game Jetpack Android untuk menambahkan dependensi library
GameActivityke filebuild.gradlegame Anda.Aktifkan prefab dengan melakukan hal berikut dengan Versi Plugin Android (AGP) 4.1+:
- Tambahkan yang berikut ke blok
androiddari filebuild.gradlemodul Anda:
buildFeatures { prefab true }- Pilih versi Prefab,
dan tetapkan ke file
gradle.properties:
android.prefabVersion=2.0.0Jika Anda menggunakan versi AGP sebelumnya, ikuti dokumentasi prefab untuk petunjuk konfigurasi yang sesuai.
- Tambahkan yang berikut ke blok
Impor library statis C/C++ atau kode sumber C/++ ke project Anda sebagai berikut.
Library statis
Dalam file
CMakeLists.txtproject Anda, impor library statisgame-activityke dalam modul prefabgame-activity_static:find_package(game-activity REQUIRED CONFIG) target_link_libraries(${PROJECT_NAME} PUBLIC log android game-activity::game-activity_static)Kode sumber
Dalam file
CMakeLists.txtproject Anda, impor paketgame-activitylalu tambahkan ke target Anda. Paketgame-activitymemerlukanlibandroid.so, jadi jika tidak ada, Anda juga harus mengimpornya.find_package(game-activity REQUIRED CONFIG) ... target_link_libraries(... android game-activity::game-activity)Selain itu, sertakan file berikut ke dalam
CmakeLists.txtproject Anda:GameActivity.cpp,GameTextInput.cpp, danandroid_native_app_glue.c.
Cara Android meluncurkan Aktivitas Anda
Sistem Android akan mengeksekusi kode dalam instance Aktivitas Anda dengan memanggil metode callback yang sesuai dengan level siklus proses aktivitas tertentu. Agar Android dapat meluncurkan aktivitas dan memulai game, Anda harus mendeklarasikan aktivitas Anda dengan atribut yang sesuai di Manifes Android. Untuk mengetahui informasi selengkapnya, lihat Pengantar Aktivitas.
Manifes Android
Setiap project aplikasi harus memiliki file AndroidManifest.xml di root set sumber project. File manifes menjelaskan informasi penting tentang aplikasi Anda ke alat build Android, sistem operasi Android, dan Google Play. Hal ini mencakup:
Nama paket dan ID aplikasi untuk mengidentifikasi game Anda secara unik di Google Play.
Komponen Aplikasi seperti aktivitas, layanan, penerima siaran, dan penyedia konten.
Izin untuk mengakses bagian sistem atau aplikasi lain yang dilindungi.
Kompatibilitas perangkat untuk menentukan persyaratan hardware dan software untuk game Anda.
Nama library native untuk
GameActivitydanNativeActivity(default-nya adalah libmain.so).
Menerapkan GameActivity dalam game Anda
Buat atau identifikasi class Java aktivitas utama Anda (yang ditentukan dalam elemen
activitydi dalam fileAndroidManifest.xml). Ubah class ini untuk memperluasGameActivitydari paketcom.google.androidgamesdk:import com.google.androidgamesdk.GameActivity; public class YourGameActivity extends GameActivity { ... }Pastikan di awal bahwa library native Anda dimuat menggunakan blok statis:
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"); } ... }Tambahkan library native Anda ke
AndroidManifest.xmljika nama library Anda bukan nama default (libmain.so):<meta-data android:name="android.app.lib_name" android:value="android-game" />
Mengimplementasikan android_main
Library
android_native_app_glueadalah library kode sumber yang digunakan game Anda untuk mengelola peristiwa siklus prosesGameActivitydi thread terpisah untuk mencegah pemblokiran di thread utama. Saat menggunakan library, Anda harus mendaftarkan callback untuk menangani peristiwa siklus proses, seperti peristiwa input sentuh. ArsipGameActivitymenyertakan versi libraryandroid_native_app_glue-nya sendiri, sehingga Anda tidak dapat menggunakan versi yang disertakan dalam rilis NDK. Jika game Anda menggunakan libraryandroid_native_app_glueyang disertakan dalam NDK, beralihlah ke versiGameActivity.Setelah Anda menambahkan kode sumber library
android_native_app_glueke project, kode tersebut akan berinteraksi denganGameActivity. Implementasikan fungsi bernamaandroid_main, yang dipanggil oleh library dan digunakan sebagai titik entri untuk game Anda. Hal tersebut akan melewati struktur yang disebutandroid_app. Hal ini mungkin berbeda untuk game dan mesin Anda. Berikut contohnya:#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; }Proses
android_appdalam game loop utama, seperti melakukan polling dan menangani peristiwa siklus aplikasi yang ditentukan di NativeAppGlueAppCmd. Misalnya, cuplikan berikut mendaftarkan fungsi_hand_cmd_proxysebagai pengendaliNativeAppGlueAppCmd, lalu melakukan polling peristiwa siklus aplikasi, dan mengirimkannya ke pengendali terdaftar (diandroid_app::onAppCmd) untuk diproses: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_pollOnce(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(); } } }Untuk membaca lebih lanjut, pelajari penerapan contoh NDK Endless Tunnel. Perbedaan utamanya adalah bagaimana cara menangani peristiwa seperti yang ditampilkan di bagian berikutnya.
Menangani peristiwa
Agar peristiwa input dapat menjangkau aplikasi Anda, buat dan daftarkan filter
peristiwa dengan android_app_set_motion_event_filter
dan android_app_set_key_event_filter.
Secara default, library native_app_glue hanya mengizinkan peristiwa gerakan dari
input
SOURCE_TOUCHSCREEN. Pastikan untuk melihat dokumen referensi
dan kode implementasi android_native_app_glue untuk mengetahui detailnya.
Untuk menangani peristiwa input, dapatkan referensi android_input_buffer dengan
android_app_swap_input_buffers()
di game loop. Referensi tersebut berisi peristiwa gerakan dan peristiwa tombol yang terjadi sejak terakhir kali
di-polling. Jumlah peristiwa yang terkandung disimpan masing-masing di motionEventsCount dan
keyEventsCount.
Lakukan iterasi dan tangani setiap peristiwa di game loop. Pada contoh ini, kode berikut melakukan iterasi
motionEventsdan menanganinya melaluihandle_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); }Lihat contoh GitHub untuk implementasi
_cookEventForPointerIndex()dan fungsi terkait lainnya.Setelah selesai, jangan lupa untuk menghapus antrean peristiwa yang baru saja Anda tangani:
android_app_clear_motion_events(mApp);
Referensi lainnya
Untuk mempelajari GameActivity lebih lanjut, lihat referensi berikut:
- Catatan rilis GameActivity dan AGDK.
- Menggunakan GameTextInput di GameActivity.
- Panduan migrasi NativeActivity.
- Dokumentasi referensi GameActivity.
- Implementasi GameActivity.
Untuk melaporkan bug atau meminta fitur baru ke GameActivity, gunakan issue tracker GameActivity.