نقل البيانات من NativeActivity جزء من Android Game Development Kit.
توضّح هذه الصفحة كيفية نقل البيانات من IDE
NativeActivity
إلى IDE
GameActivity
في مشروع لعبة Android.
يستند GameActivity
إلى NativeActivity
من إطار عمل Android
، مع تحسينات وميزات جديدة:
- يتيح استخدام
Fragment
من Jetpack. - إضافة
TextInput
لتسهيل دمج لوحة المفاتيح - تعالج أحداث اللمس والمفاتيح في فئة
GameActivity
Java بدلاً من واجهةNativeActivity
onInputEvent
.
قبل نقل البيانات، ننصحك بقراءة
دليل البدء الذي يوضّح كيفية
إعداد GameActivity
ودمجها في مشروعك.
تعديلات على نص إنشاء Java
يتم توزيع GameActivity
كأحد مكتبات
Jetpack. احرص على تطبيق خطوات تعديل نص Gradle البرمجي الموضّحة في دليل البدء:
فعِّل مكتبة Jetpack في ملف
gradle.properties
الخاص بمشروعك:android.useAndroidX=true
يمكنك اختياريًا تحديد إصدار Prefab في ملف
gradle.properties
نفسه، على سبيل المثال:android.prefabVersion=2.0.0
فعِّل ميزة Prefab في ملف
build.gradle
الخاص بتطبيقك:android { ... // other configurations buildFeatures.prefab true }
أضِف التبعية
GameActivity
إلى تطبيقك:- أضِف مكتبتَي
core
وgames-activity
. - إذا كان الحد الأدنى الحالي لمستوى واجهة برمجة التطبيقات المتوافق أقل من 16، عليك تعديله إلى 16 على الأقل.
- عدِّل إصدار حزمة تطوير البرامج (SDK) المجمّعة إلى الإصدار الذي تتطلّبه
games-activity
المكتبة. تتطلّب Jetpack عادةً استخدام أحدث إصدار من حزمة SDK في وقت إنشاء الإصدار.
قد يبدو ملف
build.gradle
المعدَّل على النحو التالي:android { compiledSdkVersion 33 ... // other configurations. defaultConfig { minSdkVersion 16 } ... // other configurations. buildFeatures.prefab true } dependencies { implementation 'androidx.core:core:1.9.0' implementation 'androidx.games:games-activity:1.2.2' }
- أضِف مكتبتَي
تعديلات على رمز Kotlin أو Java
يمكن استخدام NativeActivity
كنشاط بدء لإنشاء تطبيق يعمل بملء الشاشة. في الوقت الحالي، لا يمكن استخدام GameActivity كنشاط التمهيد. يجب أن تستنِد التطبيقات إلى فئة من GameActivity
وتستخدمها كأحد
أنشطة بدء التشغيل. يجب أيضًا إجراء تغييرات إضافية على الإعدادات ل
إنشاء تطبيق يعمل في وضع ملء الشاشة.
تفترض الخطوات التالية أنّ تطبيقك يستخدم NativeActivity
كنشاط
العملية بدء التشغيل. إذا لم يكن الأمر كذلك، يمكنك تخطّي معظمها.
أنشئ ملف Kotlin أو Java لاستضافة نشاط بدء التشغيل الجديد. على سبيل المثال، تُنشئ التعليمة البرمجية التالية
MainActivity
كنشاط بدء وي carregar carregar مكتبة التطبيق الأصلية الرئيسية،libAndroidGame.so
:Kotlin
class MainActivity : GameActivity() { override fun onResume() { super.onResume() // Use the function recommended from the following page: // https://d.android.com/training/system-ui/immersive hideSystemBars() } companion object { init { System.loadLibrary("AndroidGame") } } }
Java
public class MainActivity extends GameActivity { protected void onResume() { super.onResume(); // Use the function recommended from // https://d.android.com/training/system-ui/immersive hideSystemBars(); } static { System.loadLibrary("AndroidGame"); } }
أنشئ مظهر تطبيق بملء الشاشة في ملف
res\values\themes.xml
:<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <style name="Application.Fullscreen" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowFullscreen">true</item> <item name="android:windowContentOverlay">@null</item>" </style> </resources>
طبِّق المظهر على التطبيق في ملف
AndroidManifest.xml
:<application android:theme=”@style/Application.Fullscreen”> <!-- other configurations not listed here. --> </application>
للحصول على تعليمات مفصّلة حول وضع ملء الشاشة، يُرجى الاطّلاع على دليل التجارب الغامرة ومثال على التنفيذ في مستودع نماذج الألعاب.
لا يؤدي دليل نقل البيانات هذا إلى تغيير اسم المكتبة الأصلية. في حال تغييره، تأكَّد من اتساق أسماء المكتبات الأصلية في المواقع التالية:
رمز Kotlin أو Java:
System.loadLibrary(“AndroidGame”)
AndroidManifest.xml
:<meta-data android:name="android.app.lib_name" android:value="AndroidGame" />
داخل ملف البرنامج النصي لإنشاء C/C++، على سبيل المثال
CMakeLists.txt
:add_library(AndroidGame ...)
تعديلات على نص إنشاء C/C++
تستخدِم التعليمات الواردة في هذا القسم cmake
كمثال. إذا كان تطبيقك
يستخدم ndk-build
، عليك ربطه بالأوامر المماثلة الموضّحة في
صفحة مستندات ndk-build.
كان تطبيق GameActivity باستخدام C/C++ يقدّم إصدارًا من رمز المصدر. بالنسبة إلى الإصدار 1.2.2 والإصدارات الأحدث، يتم توفير إصدار مكتبة ثابتة. وتعدّ المكتبة static هي نوع الإصدار المُقترَح.
يتم حزم الإصدار داخل حزمة AAR باستخدام الأداة
prefab
. يتضمّن الرمز البرمجي الأصلي مصادر C/C++ لنشاط GameActivity ورمز
native_app_glue
. ويجب إنشاؤها مع رمز C/C++ الخاص
بتطبيقك.
تستخدم تطبيقات NativeActivity
حاليًا رمز native_app_glue
المُضمَّن في حزمة NDK. يجب استبداله بالإصدار native_app_glue
من GameActivity. بالإضافة إلى ذلك، تنطبق جميع cmake
الخطوات الموضّحة في
دليل البدء:
استورِد إما المكتبة الثابتة C/C++ أو رمز المصدر C/++ إلى مشروعك على النحو التالي.
مكتبة ثابتة
في ملف
CMakeLists.txt
الخاص بمشروعك، استورِد مكتبةgame-activity
static إلى وحدة الوحدات الجاهزة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)
أزِل جميع الإشارات إلى رمز
native_app_glue
في حزمة NDK، مثل:${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ... set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
إذا كنت تستخدم إصدار رمز المصدر، أدرِج ملفات
GameActivity
المصدر. وإلّا، يمكنك تخطّي هذه الخطوة.get_target_property(game-activity-include game-activity::game-activity INTERFACE_INCLUDE_DIRECTORIES) add_library(${PROJECT_NAME} SHARED main.cpp ${game-activity-include}/game-activity/native_app_glue/android_native_app_glue.c ${game-activity-include}/game-activity/GameActivity.cpp ${game-activity-include}/game-text-input/gametextinput.cpp)
حلّ مشكلة UnsatisfiedLinkError
إذا واجهت UnsatsifiedLinkError
للدالة
com.google.androidgamesdk.GameActivity.initializeNativeCode()
، أضِف
هذا الرمز إلى ملف CMakeLists.txt
:
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -u \
Java_com_google_androidgamesdk_GameActivity_initializeNativeCode")
تعديلات على رمز المصدر C/C++
اتّبِع الخطوات التالية لاستبدال مراجع NativeActivity
في
التطبيق بـ GameActivity
:
استخدِم
native_app_glue
الذي تم إصداره معGameActivity
. ابحث عن كل استخداماتandroid_native_app_glue.h
واستبدِلها بما يلي:#include <game-activity/native_app_glue/android_native_app_glue.h>
اضبط فلتر أحداث الحركة وفلتر الأحداث الرئيسية على
NULL
لكي يتمكّن تطبيقك من تلقّي أحداث الإدخال من جميع أجهزة الإدخال. يمكنك عادةً إجراء ذلك داخل دالةandroid_main()
:void android_main(android_app* app) { ... // other init code. android_app_set_key_event_filter(app, NULL); android_app_set_motion_event_filter(app, NULL); ... // additional init code, and game loop code. }
أزِل الرمز البرمجي المرتبط بـ
AInputEvent
واستبدِله بتنفيذInputBuffer
في GameActivity:while (true) { // Read all pending events. int events; struct android_poll_source* source; // If not animating, block forever waiting for events. // If animating, loop until all events are read, then continue // to draw the next frame of animation. while ((ALooper_pollOnce(engine.animating ? 0 : -1, nullptr, &events, (void**)&source)) >= 0) { // Process this app cycle or inset change event. if (source) { source->process(source->app, source); } ... // Other processing. // Check if app is exiting. if (state->destroyRequested) { engine_term_display(&engine); return; } } // Process input events if there are any. engine_handle_input(state); if (engine.animating) { // Draw a game frame. } } // Implement input event handling function. static int32_t engine_handle_input(struct android_app* app) { auto* engine = (struct engine*)app->userData; auto ib = android_app_swap_input_buffers(app); if (ib && ib->motionEventsCount) { for (int i = 0; i < ib->motionEventsCount; i++) { auto *event = &ib->motionEvents[i]; int32_t ptrIdx = 0; switch (event->action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_POINTER_UP: // Retrieve the index for the starting and the ending of any secondary pointers ptrIdx = (event->action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_UP: engine->state.x = GameActivityPointerAxes_getAxisValue( &event->pointers[ptrIdx], AMOTION_EVENT_AXIS_X); engine->state.y = GameActivityPointerAxes_getAxisValue( &event->pointers[ptrIdx], AMOTION_EVENT_AXIS_Y); break; case AMOTION_EVENT_ACTION_MOVE: // Process the move action: the new coordinates for all active touch pointers // are inside the event->pointers[]. Compare with our internally saved // coordinates to find out which pointers are actually moved. Note that there is // no index embedded inside event->action for AMOTION_EVENT_ACTION_MOVE (there // might be multiple pointers moved at the same time). ... break; } } android_app_clear_motion_events(ib); } // Process the KeyEvent in a similar way. ... return 0; }
راجِع المنطق المرتبط بملف NativeActivity's
AInputEvent
وعدِّله. كما هو موضّح في الخطوة السابقة، تتم معالجةInputBuffer
في GameActivity خارج حلقةALooper_pollOnce()
.استبدِل استخدام
android_app::activity->clazz
بandroid_app:: activity->javaGameActivity
. يعيد GameActivity تسمية مثيل JavaGameActivity
.
خطوات إضافية
تتناول الخطوات السابقة وظائف NativeActivity، ولكن GameActivity
يحتوي على
ميزات إضافية قد تريد استخدامها:
- TextInput:
- وحدة تحكّم الألعاب:
- الجزء:
- أوامر InSets لفتح نافذة جديدة محدّدة في NativeAppGlueAppCmd
ننصحك باستكشاف هذه الميزات واستخدامها بما يناسب ألعابك.
إذا كانت لديك أي أسئلة أو اقتراحات بشأن GameActivity أو مكتبات AGDK الأخرى، يمكنك إنشاء خطأ لإعلامنا بها.