เริ่มต้นใช้งาน GameActivity ซึ่งเป็นส่วนหนึ่งของ Android Game Development Kit.
คู่มือนี้อธิบายวิธีตั้งค่าและผสานรวม
GameActivity รวมถึงวิธีจัดการเหตุการณ์ในเกม Android
GameActivity ช่วยให้คุณนำเกมที่สร้างด้วย C หรือ C++ มาใช้ใน Android ได้ด้วยการทำให้กระบวนการใช้ API ที่สำคัญง่ายขึ้น
ก่อนหน้านี้ NativeActivity เป็นคลาสที่แนะนำสำหรับเกม GameActivity จะเข้ามาแทนที่ในฐานะคลาสที่แนะนำสำหรับเกม และเข้ากันได้แบบย้อนหลังกับ API ระดับ 19
ดูตัวอย่างที่ผสานรวม GameActivity ได้ที่ ที่เก็บ games-samples
ก่อนจะเริ่มต้น
ดูการเผยแพร่ GameActivity เพื่อ
รับการเผยแพร่
ตั้งค่าบิลด์ของคุณ
ใน Android นั้น Activity จะทำหน้าที่เป็นจุดเริ่มต้นของเกม และยังให้
Window สำหรับวาดด้วย เกมจำนวนมากขยาย สิ่งนี้ Activityด้วยคลาส Java หรือ Kotlin ของตนเองเพื่อขจัดข้อจำกัดใน NativeActivityขณะที่ใช้ JNIโค้ดเพื่อเชื่อมโยง กับโค้ดเกม C หรือ C++
GameActivity มีความสามารถดังต่อไปนี้
สืบทอดมาจาก
AppCompatActivityซึ่งช่วยให้คุณใช้คอมโพเนนต์สถาปัตยกรรม Android Jetpackได้แสดงผลใน
SurfaceViewที่ช่วยให้คุณ เชื่อมต่อกับองค์ประกอบ UI อื่นๆ ของ Android ได้จัดการเหตุการณ์กิจกรรม Java ซึ่งช่วยให้องค์ประกอบ UI ของ Android (เช่น
EditText, aWebViewหรือ anAd) ผสานรวมกับเกมของคุณผ่านอินเทอร์เฟซ C ได้มี C API ที่คล้ายกับ
NativeActivityและandroid_native_app_glueไลบรารี
GameActivity เผยแพร่เป็น Android Archive
(AAR) AAR นี้มีคลาส Java ที่
คุณใช้ใน
AndroidManifest.xml รวมถึงซอร์สโค้ด C
และ C++ ที่เชื่อมต่อฝั่ง Java ของ GameActivity กับการติดตั้งใช้งาน C/C++ ของแอป หากคุณใช้ GameActivity 1.2.2 ขึ้นไป ระบบจะให้ไลบรารีแบบคงที่ C/C++ ด้วย เราขอแนะนำให้คุณใช้ไลบรารีแบบคงที่แทนซอร์สโค้ดทุกครั้งที่ทำได้
รวมไฟล์ซอร์สหรือไลบรารีแบบคงที่เหล่านี้เป็นส่วนหนึ่งของกระบวนการบิลด์
ผ่าน
Prefab,
ซึ่งแสดงไลบรารีแบบเนทีฟและซอร์สโค้ดไปยัง
โปรเจ็กต์ CMake หรือ การสร้าง NDK
ทำตามวิธีการในหน้า Jetpack Android Games เพื่อเพิ่มทรัพยากร Dependency ของไลบรารี
GameActivityลงในไฟล์build.gradleของเกมเปิดใช้ Prefab โดยทำดังนี้ด้วย Android Plugin Version (AGP) 4.1 ขึ้นไป:
- เพิ่มโค้ดต่อไปนี้ลงในบล็อก
androidของไฟล์build.gradleของโมดูล
buildFeatures { prefab true }- เลือกเวอร์ชัน Prefab,
แล้วตั้งค่าลงในไฟล์
gradle.properties
android.prefabVersion=2.0.0หากคุณใช้ AGP เวอร์ชันก่อนหน้า ให้ทำตาม เอกสารประกอบของ Prefab เพื่อดูวิธีการกำหนดค่าที่เกี่ยวข้อง
- เพิ่มโค้ดต่อไปนี้ลงในบล็อก
นำเข้าไลบรารีแบบคงที่ C/C++ หรือซอร์สโค้ด C/++ ลงในโปรเจ็กต์ดังนี้
ไลบรารีแบบคงที่
ในไฟล์
CMakeLists.txtของโปรเจ็กต์ ให้นำเข้าไลบรารีแบบคงที่game-activityลงในโมดูล Prefabgame-activity_staticดังนี้find_package(game-activity REQUIRED CONFIG) target_link_libraries(${PROJECT_NAME} PUBLIC log android game-activity::game-activity_static)ซอร์สโค้ด
ในไฟล์
CMakeLists.txtของโปรเจ็กต์ ให้นำเข้าแพ็กเกจgame-activityแล้วเพิ่มลงในเป้าหมาย แพ็กเกจgame-activityต้องใช้libandroid.soดังนั้นหากไม่มี คุณต้องนำเข้าแพ็กเกจนี้ด้วยfind_package(game-activity REQUIRED CONFIG) ... target_link_libraries(... android game-activity::game-activity)นอกจากนี้ ให้รวมไฟล์
GameActivity.cpp,GameTextInput.cppและandroid_native_app_glue.cลงในCmakeLists.txtของโปรเจ็กต์ด้วย
วิธีที่ Android เปิดใช้กิจกรรม
ระบบ Android จะเรียกใช้โค้ดในอินสแตนซ์กิจกรรมโดยเรียกใช้เมธอดเรียกกลับที่สอดคล้องกับระยะต่างๆ ของวงจรกิจกรรม หากต้องการให้ Android เปิดใช้กิจกรรมและเริ่มเกม คุณต้องประกาศกิจกรรมด้วยแอตทริบิวต์ที่เหมาะสมในไฟล์ Manifest ของ Android ดูข้อมูลเพิ่มเติมได้ที่ ข้อมูลเบื้องต้นเกี่ยวกับกิจกรรม
ไฟล์ Manifest ของ Android
ทุกโปรเจ็กต์แอปต้องมีไฟล์ AndroidManifest.xml ที่ รูทของชุดซอร์สโค้ดของโปรเจ็กต์ ไฟล์ Manifest อธิบายข้อมูลสำคัญเกี่ยวกับแอปให้กับเครื่องมือสร้าง Android, ระบบปฏิบัติการ Android และ Google Play ซึ่งรวมถึงสิ่งต่อไปนี้
ชื่อแพ็กเกจและรหัสแอป เพื่อระบุเกมของคุณใน Google Play ได้อย่างไม่ซ้ำกัน
คอมโพเนนต์ของแอป เช่น กิจกรรม บริการ ตัวรับสัญญาณบรอดแคสต์ และผู้ให้บริการเนื้อหา
สิทธิ์เข้าถึง ส่วนที่ได้รับการปกป้องของระบบหรือแอปอื่นๆ
ความเข้ากันได้ของอุปกรณ์ เพื่อระบุข้อกำหนดด้านฮาร์ดแวร์และซอฟต์แวร์สำหรับเกม
ชื่อไลบรารีแบบเนทีฟสำหรับ
GameActivityและNativeActivity(ค่าเริ่มต้นคือ libmain.so)
ติดตั้งใช้งาน GameActivity ในเกม
สร้างหรือระบุคลาส Java ของกิจกรรมหลัก (คลาสที่ระบุในองค์ประกอบ
activityภายในไฟล์AndroidManifest.xml) เปลี่ยนคลาสนี้ให้ขยายGameActivityจากแพ็กเกจcom.google.androidgamesdkดังนี้import com.google.androidgamesdk.GameActivity; public class YourGameActivity extends GameActivity { ... }ตรวจสอบว่าได้โหลดไลบรารีแบบเนทีฟเมื่อเริ่มต้นโดยใช้บล็อกแบบคงที่
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"); } ... }เพิ่มไลบรารีแบบเนทีฟลงใน
AndroidManifest.xmlหากชื่อไลบรารีไม่ใช่ชื่อเริ่มต้น (libmain.so)<meta-data android:name="android.app.lib_name" android:value="android-game" />
ติดตั้งใช้งาน android_main
ไลบรารี
android_native_app_glueเป็นไลบรารีซอร์สโค้ดที่เกมใช้เพื่อจัดการเหตุการณ์วงจรการทำงานของGameActivityในเธรดแยกต่างหากเพื่อป้องกันการบล็อกในเธรดหลัก เมื่อใช้ไลบรารี คุณจะลงทะเบียนการเรียกกลับเพื่อจัดการเหตุการณ์วงจรการทำงาน เช่น เหตุการณ์การป้อนข้อมูลด้วยการสัมผัส ที่เก็บGameActivityมีไลบรารีandroid_native_app_glueเวอร์ชันของตัวเอง ดังนั้นคุณจึงใช้เวอร์ชันที่รวมอยู่ในการเผยแพร่ NDK ไม่ได้ หากเกมของคุณใช้ไลบรารีandroid_native_app_glueที่รวมอยู่ใน NDK ให้เปลี่ยนไปใช้เวอร์ชันGameActivityหลังจากเพิ่มซอร์สโค้ดไลบรารี
android_native_app_glueลงในโปรเจ็กต์แล้ว ไลบรารีจะเชื่อมต่อกับGameActivityติดตั้งใช้งานฟังก์ชันที่ชื่อว่าandroid_mainซึ่ง ไลบรารีจะเรียกใช้และใช้เป็นจุดเริ่มต้นของเกม ฟังก์ชันนี้จะรับโครงสร้างที่เรียกว่าandroid_appซึ่งอาจแตกต่างกันไปตามเกมและเอนจิน ตัวอย่าง#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; }ประมวลผล
android_appในลูปเกมหลัก เช่น การโพลและจัดการ เหตุการณ์วงจรแอปที่กำหนดไว้ใน NativeAppGlueAppCmd ตัวอย่างเช่น ข้อมูลโค้ดต่อไปนี้จะลงทะเบียนฟังก์ชัน_hand_cmd_proxyเป็นตัวแฮนเดิลNativeAppGlueAppCmdจากนั้นจะโพลเหตุการณ์วงจรแอปและส่งเหตุการณ์ไปยังตัวแฮนเดิลที่ลงทะเบียนไว้(ในandroid_app::onAppCmd) เพื่อประมวลผล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(); } } }หากต้องการอ่านเพิ่มเติม ให้ศึกษาการติดตั้งใช้งานตัวอย่าง Endless Tunnel NDK ความแตกต่างหลักคือวิธีจัดการเหตุการณ์ตามที่แสดงในส่วนถัดไป
จัดการเหตุการณ์
หากต้องการให้เหตุการณ์การป้อนข้อมูลเข้าถึงแอป ให้สร้างและลงทะเบียนตัวกรองเหตุการณ์
ด้วย android_app_set_motion_event_filter
และ android_app_set_key_event_filter
โดยค่าเริ่มต้น ไลบรารี native_app_glue จะอนุญาตเฉพาะเหตุการณ์การเคลื่อนไหวจากการป้อนข้อมูล
SOURCE_TOUCHSCREEN
โปรดดูรายละเอียดใน เอกสารอ้างอิง
และโค้ดการติดตั้งใช้งาน android_native_app_glue
หากต้องการจัดการเหตุการณ์การป้อนข้อมูล ให้รับการอ้างอิงไปยัง android_input_buffer ด้วย
android_app_swap_input_buffers()
ในลูปเกม บัฟเฟอร์เหล่านี้มีเหตุการณ์การเคลื่อนไหวและเหตุการณ์สำคัญที่เกิดขึ้นนับตั้งแต่ครั้งล่าสุดที่
โพล จำนวนเหตุการณ์ที่รวมอยู่จะจัดเก็บไว้ใน motionEventsCount และ keyEventsCount ตามลำดับ
วนซ้ำและจัดการแต่ละเหตุการณ์ในลูปเกม ในตัวอย่างนี้ โค้ดต่อไปนี้จะวนซ้ำ
motionEventsและจัดการเหตุการณ์ผ่านhandle_eventandroid_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); }ดูตัวอย่าง GitHub สำหรับการติดตั้งใช้งาน
_cookEventForPointerIndex()และฟังก์ชันอื่นๆ ที่เกี่ยวข้องเมื่อเสร็จแล้ว อย่าลืมล้างคิวเหตุการณ์ที่คุณเพิ่งจัดการ
android_app_clear_motion_events(mApp);
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ GameActivity ได้ที่
- บันทึกประจำรุ่นของ GameActivity และ AGDK
- ใช้ GameTextInput ใน GameActivity
- คำแนะนำในการย้ายข้อมูล NativeActivity
- เอกสารอ้างอิงของ GameActivity
- การติดตั้งใช้งาน GameActivity
หากต้องการรายงานข้อบกพร่องหรือส่งคำขอฟีเจอร์ใหม่สำหรับ GameActivity ให้ใช้ เครื่องมือติดตามปัญหาของ GameActivity