بدء استخدام GameActivity جزء من حزمة تطوير ألعاب Android.
يصف هذا الدليل كيفية إعداد ودمج
GameActivity
والتعامل مع الأحداث في جهاز Android
في اللعبة.
يساعدك GameActivity
في الحصول على C أو
لعبة C++ إلى Android من خلال تبسيط عملية استخدام واجهات برمجة التطبيقات المهمة.
كانت قيمة NativeActivity
في السابق
الفئة الموصى بها للألعاب. يحل GameActivity
محله باعتباره الموصى به
للألعاب، وتتوافق مع الإصدارات القديمة مع المستوى 19 من واجهة برمجة التطبيقات.
للاطّلاع على نموذج يدمج GameActivity، يمكنك الاطّلاع على مستودع نماذج الألعاب.
قبل البدء
عرض إصدارات GameActivity
من أجل
للحصول على التوزيع.
إعداد تصميمك
على Android، يتم استخدام Activity
كإدخال
نقطة للّعبة، كما أنه يوفر
Window
للرسم بالداخل. ألعاب عديدة توسِّع نطاقها
Activity
مع فئة Java أو Kotlin الخاصة به للتغلب على القيود في
NativeActivity
أثناء استخدام رمز JNI
لإجراء الربط
إلى رمز اللعبة C أو C++.
يوفّر GameActivity
الإمكانات التالية:
مكتسب من
AppCompatActivity
، السماح لك باستخدام بنية Android Jetpack المكوّنات.تظهر في شكل
SurfaceView
التي تتيح لك مع أي عنصر آخر في واجهة مستخدم Android.تتعامل مع أحداث أنشطة Java. يسمح ذلك لأي عنصر من عناصر واجهة المستخدم في 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++. إذا كنت تستخدم الإصدار 1.2.2 من GameActivity
أو إصدارًا أحدث، سيتم نقل لغة C/C++
كما تتوفر مكتبة ثابتة. ننصحك باستخدام هذه الميزة، متى أمكن ذلك
المكتبة الثابتة بدلاً من رمز المصدر.
قم بتضمين ملفات المصدر هذه أو المكتبة الثابتة كجزء من
عملية البناء من خلال
Prefab
،
والذي يكشف المكتبات الأصلية ورمز المصدر
CMake project أو إصدار NDK.
اتّبِع التعليمات الواردة في صفحة ألعاب Jetpack Android Games لإضافة
GameActivity
اعتماد المكتبة لملفbuild.gradle
الخاص باللعبةقم بتفعيل ميزة Prefab من خلال تنفيذ ما يلي باستخدام إصدار Android الإضافي (AGP) 4.1 أو الإصدارات الأحدث:
- أضف ما يلي إلى كتلة
android
من ملفbuild.gradle
في الوحدة:
buildFeatures { prefab true }
- اختر إصدارًا مسبقًا،
واضبطه على ملف
gradle.properties
:
android.prefabVersion=2.0.0
إذا كنت تستخدم إصدارات سابقة من AGP، اتّبِع الخطوات التالية: مستندات جاهزة للحصول على تعليمات الضبط المقابلة.
- أضف ما يلي إلى كتلة
استيراد مكتبة C/C++ الثابتة أو رمز المصدر C/++ إلى مشروعك على النحو التالي.
مكتبة ثابتة
في ملف
CMakeLists.txt
الخاص بمشروعك، استورِد العنصر الثابتgame-activity
. مكتبة في وحدة الإعداد المسبقgame-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 رمزًا برمجيًا في مثيل النشاط من خلال استدعاء معاودة الاتصال والطرق التي تتوافق مع مراحل محددة من دورة حياة النشاط. بالترتيب ليطلق Android نشاطك ويبدأ لعبتك، عليك الإفصاح نشاطك باستخدام السمات المناسبة في بيان Android. لمزيد من المعلومات، المعلومات، راجع مقدمة عن الأنشطة.
بيان Android
يجب أن يكون لكل مشروع تطبيق AndroidManifest.xml في جذر مجموعة مصدر المشروع. يصف ملف البيان البيانات الأساسية المعلومات حول تطبيقك إلى أدوات تصميم 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
" المضمّنة في اتفاقية عدم الإفصاح، يُرجى التبديل إلى إصدار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.