با قسمت GameActivity از کیت توسعه بازی اندروید شروع کنید.
این راهنما نحوه راه اندازی و ادغام GameActivity
و مدیریت رویدادها را در بازی اندروید خود شرح می دهد.
GameActivity
به شما کمک می کند تا با ساده کردن فرآیند استفاده از API های حیاتی، بازی C یا C++ خود را به اندروید بیاورید. قبلاً NativeActivity
کلاس توصیه شده برای بازی ها بود. GameActivity
به عنوان کلاس توصیه شده برای بازی ها جایگزین آن می شود و با سطح API 19 سازگار است.
برای نمونه ای که GameActivity را ادغام می کند، به مخزن بازی-نمونه ها مراجعه کنید.
قبل از شروع
برای دریافت توزیع ، نسخههای GameActivity
را ببینید.
ساخت خود را تنظیم کنید
در اندروید، یک Activity
به عنوان نقطه ورود برای بازی شما عمل میکند و همچنین Window
برای ترسیم درون آن فراهم میکند. بسیاری از بازیها این Activity
با کلاس جاوا یا کاتلین خود گسترش میدهند تا محدودیتهای NativeActivity
را از بین ببرند در حالی که از کد JNI
برای پل زدن به کد بازی C یا C++ خود استفاده میکنند.
GameActivity
قابلیت های زیر را ارائه می دهد:
از
AppCompatActivity
به ارث می رسد و به شما امکان می دهد از اجزای معماری Jetpack Android استفاده کنید.به یک
SurfaceView
رندر میشود که به شما امکان میدهد با هر عنصر رابط کاربری اندرویدی دیگر ارتباط برقرار کنید.رویدادهای فعالیت جاوا را مدیریت می کند. این اجازه می دهد تا هر عنصر رابط کاربری Android (مانند
EditText
،WebView
یاAd
) از طریق یک رابط C به بازی شما ادغام شود.یک C API مشابه
NativeActivity
و کتابخانهandroid_native_app_glue
ارائه می دهد.
GameActivity
به عنوان یک آرشیو Android (AAR) توزیع شده است. این AAR شامل کلاس جاوا است که در AndroidManifest.xml
خود استفاده میکنید، و همچنین کد منبع C و C++ که سمت جاوا GameActivity
را به پیادهسازی C/C++ برنامه متصل میکند. اگر از GameActivity
1.2.2 یا جدیدتر استفاده می کنید، کتابخانه ثابت C/C++ نیز ارائه شده است. در صورت امکان، توصیه می کنیم به جای کد منبع، از کتابخانه ایستا استفاده کنید.
این فایل های منبع یا کتابخانه استاتیک را به عنوان بخشی از فرآیند ساخت خود از طریق Prefab
وارد کنید، که کتابخانه های بومی و کد منبع را در معرض پروژه CMake یا ساخت NDK شما قرار می دهد.
دستورالعملهای صفحه Jetpack Android Games را دنبال کنید تا وابستگی کتابخانه
GameActivity
را به فایلbuild.gradle
بازی خود اضافه کنید.با انجام کارهای زیر با Android Plugin Version (AGP) 4.1+ ، prefab را فعال کنید:
- موارد زیر را به بلوک
android
فایلbuild.gradle
ماژول خود اضافه کنید:
buildFeatures { prefab true }
- یک نسخه Prefab را انتخاب کنید و آن را روی فایل
gradle.properties
تنظیم کنید:
android.prefabVersion=2.0.0
اگر از نسخه های قبلی AGP استفاده می کنید، مستندات پیش ساخته را برای دستورالعمل های پیکربندی مربوطه دنبال کنید.
- موارد زیر را به بلوک
کتابخانه ایستا 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 Activity شما را راه اندازی می کند
سیستم Android کد را در نمونه Activity شما با فراخوانی روشهای برگشتی که مربوط به مراحل خاصی از چرخه حیات فعالیت هستند، اجرا میکند. برای اینکه اندروید فعالیت شما را راه اندازی کند و بازی شما را شروع کند، باید فعالیت خود را با ویژگی های مناسب در مانیفست اندروید اعلام کنید. برای اطلاعات بیشتر، به مقدمه فعالیت ها مراجعه کنید.
مانیفست اندروید
هر پروژه برنامه باید یک فایل AndroidManifest.xml در ریشه مجموعه منبع پروژه داشته باشد. فایل مانیفست اطلاعات ضروری در مورد برنامه شما را برای ابزارهای ساخت Android، سیستم عامل Android و Google Play توضیح می دهد. این شامل:
نام بسته و شناسه برنامه برای شناسایی منحصر به فرد بازی خود در Google Play.
اجزای برنامه مانند فعالیت ها، خدمات، گیرنده های پخش و ارائه دهندگان محتوا.
مجوزهای دسترسی به بخش های محافظت شده سیستم یا سایر برنامه ها.
سازگاری دستگاه برای تعیین سخت افزار و نرم افزار مورد نیاز بازی شما.
نام کتابخانه بومی
GameActivity
وNativeActivity
( پیشفرض libmain.so است ).
GameActivity را در بازی خود پیاده کنید
کلاس Java Activity اصلی خود را ایجاد یا شناسایی کنید (کلاس مشخص شده در عنصر
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"); } ... }
اگر نام کتابخانه شما نام پیش فرض نیست (
libmain.so
) کتابخانه بومی خود را بهAndroidManifest.xml
اضافه کنید:<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_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(); } } }
برای مطالعه بیشتر، اجرای مثال 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_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 استفاده کنید.