เริ่มต้นใช้งาน 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
,WebView
หรือAd
) สามารถ ผสานรวมเข้ากับเกมผ่านอินเทอร์เฟซ 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 เพื่อเพิ่มการอ้างอิงไลบรารี
GameActivity
ลงในไฟล์build.gradle
ของเกมเปิดใช้ Prefab โดยทำดังนี้ด้วย ปลั๊กอิน Android เวอร์ชัน (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
ลงในโมดูลgame-activity_static
prefab ดังนี้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)
นอกจากนี้ ให้รวมไฟล์ต่อไปนี้ไว้ใน
CmakeLists.txt
ของโปรเจ็กต์GameActivity.cpp
,GameTextInput.cpp
และandroid_native_app_glue.c
วิธีที่ Android เปิดใช้กิจกรรม
ระบบ Android จะเรียกใช้โค้ดในอินสแตนซ์ของกิจกรรมโดยการเรียกใช้เมธอด Callback ที่สอดคล้องกับขั้นตอนที่เฉพาะเจาะจงของวงจรกิจกรรม หากต้องการให้ Android เปิดใช้งานกิจกรรมและเริ่มเกม คุณต้องประกาศกิจกรรมด้วยแอตทริบิวต์ที่เหมาะสมใน Android Manifest ดูข้อมูลเพิ่มเติมได้ที่ข้อมูลเบื้องต้นเกี่ยวกับกิจกรรม
ไฟล์ 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(); } } }
หากต้องการอ่านเพิ่มเติม ให้ศึกษาการใช้งานตัวอย่าง NDK ของ Endless Tunnel ความแตกต่างหลักๆ จะอยู่ที่วิธีจัดการเหตุการณ์ดังที่แสดงใน ส่วนถัดไป
จัดการเหตุการณ์
หากต้องการให้เหตุการณ์อินพุตเข้าถึงแอป ให้สร้างและลงทะเบียนตัวกรองเหตุการณ์ด้วย android_app_set_motion_event_filter
และ android_app_set_key_event_filter
โดยค่าเริ่มต้น native_app_glue
library จะอนุญาตเฉพาะเหตุการณ์การเคลื่อนไหวจากอินพุต SOURCE_TOUCHSCREEN
เท่านั้น โปรดดูรายละเอียดในเอกสารอ้างอิง
และโค้ดการใช้งานandroid_native_app_glue
หากต้องการจัดการเหตุการณ์อินพุต ให้รับการอ้างอิงถึง android_input_buffer
ด้วย
android_app_swap_input_buffers()
ในลูปเกม ซึ่งประกอบด้วยเหตุการณ์การเคลื่อนไหวและเหตุการณ์สำคัญที่เกิดขึ้นนับตั้งแต่ครั้งล่าสุดที่ระบบ
ทำการสำรวจ ระบบจะจัดเก็บจำนวนเหตุการณ์ที่มีอยู่ใน motionEventsCount
และ
keyEventsCount
ตามลำดับ
วนซ้ำและจัดการแต่ละเหตุการณ์ในลูปเกม ในตัวอย่างนี้ โค้ดต่อไปนี้จะวนซ้ำ
motionEvents
และจัดการผ่านhandle_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); }
ดูการใช้งาน
_cookEventForPointerIndex()
และฟังก์ชันอื่นๆ ที่เกี่ยวข้องได้ในตัวอย่าง GitHubเมื่อเสร็จแล้ว อย่าลืมล้างคิวของเหตุการณ์ที่คุณเพิ่งจัดการ
android_app_clear_motion_events(mApp);
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ GameActivity
ได้ที่หัวข้อต่อไปนี้
- บันทึกประจำรุ่นของ GameActivity และ AGDK
- ใช้ GameTextInput ใน GameActivity
- คำแนะนำในการย้ายข้อมูล NativeActivity
- เอกสารประกอบข้อมูลอ้างอิงของ GameActivity
- การใช้งาน GameActivity
หากต้องการรายงานข้อบกพร่องหรือขอฟีเจอร์ใหม่สำหรับ GameActivity ให้ใช้เครื่องมือติดตามปัญหาของ GameActivity