เริ่มต้นใช้งาน GameActivity เป็นส่วนหนึ่งของชุดเครื่องมือพัฒนาเกม Android
คู่มือนี้จะอธิบายวิธีการตั้งค่าและผสานรวม
GameActivity
และจัดการกิจกรรมต่างๆ ใน Android
เกม
GameActivity
ช่วยให้คุณนำ C หรือ
เกม C++ สำหรับ Android ด้วยการทำให้กระบวนการใช้ API ที่สำคัญง่ายขึ้น
ก่อนหน้านี้ NativeActivity
คือ
คลาสที่แนะนำสำหรับเกม GameActivity
แทนที่ค่านี้ตามที่แนะนำ
สำหรับเกม และเข้ากันได้กับ API ระดับ 19
สำหรับตัวอย่างที่ผสานรวม GameActivity โปรดดูที่ ที่เก็บตัวอย่างเกม
ก่อนจะเริ่มต้น
ดู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
(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 (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)
และรวมไฟล์ต่อไปนี้ลงใน
CmakeLists.txt
ของโปรเจ็กต์ด้วยGameActivity.cpp
,GameTextInput.cpp
และandroid_native_app_glue.c
วิธีที่ Android เปิดตัวกิจกรรมของคุณ
ระบบ Android จะเรียกใช้โค้ดในอินสแตนซ์กิจกรรมโดยการเรียกใช้ Callback ที่สอดคล้องกับระยะที่เจาะจงในวงจรกิจกรรม อยู่ในคำสั่งซื้อ สำหรับ Android เพื่อเปิดกิจกรรมและเริ่มเกม คุณจะต้องประกาศ กิจกรรมของคุณด้วยแอตทริบิวต์ที่เหมาะสมในไฟล์ Android Manifest สำหรับข้อมูลเพิ่มเติม ดูข้อมูลได้ที่ ข้อมูลเบื้องต้นเกี่ยวกับกิจกรรม
ไฟล์ Manifest ของ Android
ทุกโปรเจ็กต์ของแอปต้องมี AndroidManifest.xml ที่ รูทของชุดแหล่งที่มาของโปรเจ็กต์ ไฟล์ Manifest อธิบายถึงสิ่งสำคัญ ข้อมูลแอปของคุณไปยังเครื่องมือสร้างของ 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
เหตุการณ์ในชุดข้อความแยกต่างหากใน เพื่อป้องกันการบล็อกในเทรดหลัก เมื่อใช้ไลบรารี คุณลงทะเบียน Callback เพื่อจัดการเหตุการณ์ในวงจร เช่น การป้อนข้อมูลด้วยการสัมผัส กิจกรรม ที่เก็บถาวร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
ใน Game Loop หลักของคุณ เช่น แบบสำรวจและการจัดการ เหตุการณ์ในวงจรของแอปที่กำหนดไว้ใน 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_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(); } } }
หากต้องการอ่านเพิ่มเติม ให้ศึกษาการใช้งานอุโมงค์ข้อมูลไม่มีที่สิ้นสุด (Endless Tunnel) ตัวอย่าง NDK ความแตกต่างหลักๆ คือวิธีจัดการเหตุการณ์ดังที่แสดงใน หัวข้อถัดไป
จัดการเหตุการณ์
หากต้องการเปิดใช้เหตุการณ์การป้อนข้อมูลเพื่อเข้าถึงแอปของคุณ ให้สร้างและลงทะเบียนเหตุการณ์
ตัวกรองที่มี android_app_set_motion_event_filter
และ android_app_set_key_event_filter
โดยค่าเริ่มต้น ไลบรารี native_app_glue
จะอนุญาตเฉพาะเหตุการณ์การเคลื่อนไหวจาก
หน้าจอสัมผัสต้นทาง
อินพุต อย่าลืมดูเอกสารอ้างอิง
และรหัสการใช้ android_native_app_glue
เพื่อดูรายละเอียด
หากต้องการจัดการเหตุการณ์การป้อนข้อมูล โปรดดู android_input_buffer
ด้วย
android_app_swap_input_buffers()
ใน Game Loop ของคุณ ซึ่งมีเหตุการณ์การเคลื่อนไหวและเหตุการณ์สําคัญที่เกิดขึ้นตั้งแต่ครั้งล่าสุด
มีแบบสำรวจ จำนวนเหตุการณ์ที่จัดเก็บไว้ใน motionEventsCount
และ
keyEventsCount
ตามลำดับ
ทำซ้ำและจัดการแต่ละเหตุการณ์ใน Game Loop ในตัวอย่างนี้ โค้ดต่อไปนี้จะทำซ้ำ
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); }
โปรดดู ตัวอย่างของ GitHub สำหรับการใช้
_cookEventForPointerIndex()
และ ที่เกี่ยวข้องเมื่อคุณทำเสร็จแล้ว อย่าลืมล้างคิวของกิจกรรมที่คุณเพิ่ง ที่จัดการ:
android_app_clear_motion_events(mApp);
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ GameActivity
ได้ที่บทความต่อไปนี้
- บันทึกประจำรุ่นของ GameActivity และ AGDK
- ใช้ GameTextInput ใน GameActivity
- คำแนะนำในการย้ายข้อมูล NativeActivity
- เอกสารประกอบอ้างอิงเกี่ยวกับ GameActivity
- การใช้งาน GameActivity
หากต้องการรายงานข้อบกพร่องหรือขอฟีเจอร์ใหม่ไปยัง GameActivity ให้ใช้เครื่องมือติดตามปัญหา GameActivity