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
SurfaceView
yang 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
GameActivity
ke filebuild.gradle
game Anda.Aktifkan prefab dengan melakukan hal berikut dengan Versi Plugin Android (AGP) 4.1+:
- Tambahkan yang berikut ke blok
android
dari filebuild.gradle
modul Anda:
buildFeatures { prefab true }
- Pilih versi Prefab,
dan tetapkan ke file
gradle.properties
:
android.prefabVersion=2.0.0
Jika 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.txt
project Anda, impor library statisgame-activity
ke 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.txt
project Anda, impor paketgame-activity
lalu tambahkan ke target Anda. Paketgame-activity
memerlukanlibandroid.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.txt
project 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
GameActivity
danNativeActivity
(default-nya adalah libmain.so).
Menerapkan GameActivity dalam game Anda
Buat atau identifikasi class Java aktivitas utama Anda (yang ditentukan dalam elemen
activity
di dalam fileAndroidManifest.xml
). Ubah class ini untuk memperluasGameActivity
dari 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.xml
jika 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_glue
adalah library kode sumber yang digunakan game Anda untuk mengelola peristiwa siklus prosesGameActivity
di thread terpisah untuk mencegah pemblokiran di thread utama. Saat menggunakan library, Anda mendaftarkan callback untuk menangani peristiwa siklus proses, seperti input sentuh peristiwa. ArsipGameActivity
menyertakan 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_glue
yang disertakan dalam NDK, beralihlah ke versiGameActivity
.Setelah Anda menambahkan kode sumber library
android_native_app_glue
ke 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_app
dalam game loop utama, seperti melakukan polling dan menangani peristiwa siklus aplikasi yang ditentukan di NativeAppGlueAppCmd. Misalnya, cuplikan berikut mendaftarkan fungsi_hand_cmd_proxy
sebagai 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_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(); } } }
Untuk membaca lebih lanjut, pelajari implementasi Endless Tunnel Contoh NDK. 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
motionEvents
dan 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.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.