حلّ مشاكل LMKs في لعبة Unity هو عملية منهجية:
الحصول على لقطة للذاكرة
استخدِم أداة Unity Profiler للحصول على لقطة للذاكرة التي تديرها Unity. يوضّح الشكل 2 طبقات إدارة الذاكرة التي تستخدمها Unity للتعامل مع الذاكرة في لعبتك.
الذاكرة المُدارة
تطبّق إدارة الذاكرة في Unity طبقة ذاكرة خاضعة للتحكّم تستخدم مجموعة عناصر مُدارة وأداة جمع البيانات المهملة لتخصيص الذاكرة وتعيينها تلقائيًا. نظام الذاكرة المُدارة هو بيئة كتابة نصوص برمجية بلغة C# تستند إلى Mono أو IL2CPP. تتمثل ميزة نظام الذاكرة المُدارة في أنّه يستخدم أداة جمع البيانات المهملة لتحرير تخصيصات الذاكرة تلقائيًا.
الذاكرة غير المُدارة بلغة C#
توفّر طبقة الذاكرة غير المُدارة بلغة C# إمكانية الوصول إلى طبقة الذاكرة الأصلية، ما يتيح التحكّم الدقيق في تخصيصات الذاكرة أثناء استخدام رمز C#. يمكن الوصول إلى طبقة إدارة الذاكرة هذه من خلال مساحة الاسم Unity.Collections ومن خلال دوال مثل UnsafeUtility.Malloc وUnsafeUtility.Free.
الذاكرة الأصلية
يستخدم جوهر C/C++ الداخلي في Unity نظام ذاكرة أصلية لـ إدارة المشاهد ومواد العرض وواجهات برمجة التطبيقات للرسومات وبرامج التشغيل والأنظمة الفرعية ومخازن المكوّنات الإضافية. على الرغم من أنّ الوصول المباشر محدود، يمكنك معالجة البيانات بأمان باستخدام واجهة برمجة التطبيقات C# من Unity والاستفادة من الرموز البرمجية الأصلية الفعّالة. نادرًا ما تتطلب الذاكرة الأصلية تفاعلاً مباشرًا، ولكن يمكنك مراقبة تأثير الذاكرة الأصلية على الأداء باستخدام أداة Profiler وضبط الإعدادات لتحسين الأداء.
لا تتم مشاركة الذاكرة بين رمز C# والرموز البرمجية الأصلية كما هو موضّح في الشكل 3. يتم تخصيص البيانات التي يتطلبها رمز C# في مساحة الذاكرة المُدارة في كل مرة تكون هناك حاجة إليها.
لكي يتمكّن رمز اللعبة المُدارة (C#) من الوصول إلى بيانات الذاكرة الأصلية للمحرّك، على سبيل المثال، يؤدي طلب GameObject.transform إلى إجراء طلب أصلي للوصول إلى بيانات الذاكرة في المنطقة الأصلية، ثم يتم عرض القيم على C# باستخدام Bindings. تضمن الروابط اصطلاحات الاتصال المناسبة لكل منصة وتتولى عملية الترتيب التلقائي للأنواع المُدارة إلى ما يعادلها من الأنواع الأصلية.
يحدث ذلك في المرة الأولى فقط، لأنّ الغلاف المُدار للوصول إلى السمة transform يتم الاحتفاظ به في الرموز البرمجية الأصلية. يمكن أن يؤدي تخزين السمة transform مؤقتًا إلى تقليل عدد عمليات الاتصال المتبادلة بين الرمز المُدار والرموز البرمجية الأصلية، ولكن تعتمد فائدة التخزين المؤقت على عدد مرات استخدام السمة. تجدر الإشارة أيضًا إلى أنّ Unity لا تنسخ أجزاء من الذاكرة الأصلية إلى الذاكرة المُدارة عند الوصول إلى واجهات برمجة التطبيقات هذه.
لمزيد من المعلومات، يُرجى الرجوع إلى مقدّمة عن الذاكرة في Unity.
بالإضافة إلى ذلك، من الضروري وضع ميزانية للذاكرة للحفاظ على تشغيل لعبتك بسلاسة، ويضمن تطبيق نظام تحليلات أو إعداد تقارير عن استهلاك الذاكرة عدم تجاوز كل إصدار جديد ميزانية الذاكرة. يُعد دمج اختبارات وضع التشغيل مع التكامل المستمر (CI) للتحقّق من استهلاك الذاكرة في مناطق معيّنة من اللعبة استراتيجية أخرى للحصول على إحصاءات أفضل.
إدارة مواد العرض
هذا هو الجزء الأكثر تأثيرًا وقابلية للتنفيذ في استهلاك الذاكرة. ننصحك بإنشاء ملف تعريف في أقرب وقت ممكن.
يمكن أن يختلف استخدام الذاكرة في ألعاب Android بشكل كبير حسب نوع اللعبة وعدد مواد العرض وأنواعها واستراتيجيات تحسين الذاكرة. ومع ذلك، تشمل العوامل الشائعة التي تساهم في استخدام الذاكرة عادةً الزخارف والشبكات والملفات الصوتية والتظليلات والصور المتحركة والنصوص البرمجية.
رصد مواد العرض المكرّرة
تتمثل الخطوة الأولى في رصد مواد العرض التي تم إعدادها بشكل غير جيد ومواد العرض المكرّرة باستخدام أداة إنشاء ملفات تعريف الذاكرة أو أداة إعداد تقارير الإصدار أو أداة Project Auditor.
زخارف
حلِّل الأجهزة التي تتوافق مع لعبتك وحدِّد تنسيق الزخرفة الصحيح. يمكنك تقسيم حِزم الزخارف للأجهزة المتطورة والأجهزة المنخفضة الإمكانات باستخدام ميزة عرض المواد في Play أو Addressable أو عملية أكثر يدوية باستخدام AssetBundle.
اتّبِع الاقتراحات الأكثر شيوعًا المتوفّرة في مقالة تحسين أداء لعبتك على الأجهزة الجوّالة ومنشور المناقشة تحسين إعدادات استيراد الزخارف في Unity. بعد ذلك، جرِّب هذه الحلول:
اضغط الزخارف باستخدام تنسيقات ASTC للحصول على مساحة أصغر من الذاكرة ، وجرِّب معدّل حِزم أعلى، مثل 8×8.
إذا كان استخدام ETC2 مطلوبًا، اجمِع زخارفك في Atlas. يضمن وضع زخارف متعدّدة في زخرفة واحدة أن تكون قوتها من مضاعفات الرقم 2، ويمكن أن يقلّل من طلبات الرسم، ويمكن أن يسرّع عملية العرض.
حسِّن تنسيق وحجم زخرفة RenderTarget. تجنَّب الزخارف ذات الدقة العالية بشكل غير ضروري. يؤدي استخدام زخارف أصغر على الأجهزة الجوّالة إلى توفير الذاكرة.
استخدِم ميزة Texture channel packing لتوفير ذاكرة الزخرفة.
الشبكات والنماذج
ابدأ بالتحقّق من الإعدادات الأساسية (الصفحة 27) و تأكَّد من إعدادات استيراد الشبكة المتداخلة هذه:
- اجمِع الشبكات الأصغر حجمًا والمكرّرة.
- قلِّل عدد رؤوس الأشكال في المشاهد (على سبيل المثال، الأشكال الثابتة أو البعيدة).
- أنشئ مجموعات مستوى التفاصيل (LOD) لمواد العرض ذات الهندسة العالية.
المواد والتظليلات
- أزِل أشكال التظليل غير المستخدَمة برمجيًا أثناء عملية التصميم.
- اجمِع أشكال التظليل المستخدَمة بشكل متكرر في تظليلات شاملة لتجنُّب تكرار التظليل.
- فعِّل ميزة التحميل الديناميكي للتظليل لمعالجة مساحة الذاكرة الكبيرة للتظليلات التي تم تحميلها مسبقًا في ذاكرة الوصول العشوائي للرسومات (VRAM)/ذاكرة الوصول العشوائي (RAM). ومع ذلك، انتبه إذا كان تجميع أداة التظليل يؤدي إلى حدوث مشاكل في الإطارات.
- استخدِم ميزة التحميل الديناميكي للتظليل لمنع تحميل جميع الأشكال. لمزيد من المعلومات، يُرجى الرجوع إلى منشور المدوّنة تحسُّن أوقات إنشاء أداة التظليل و استخدام الذاكرة.
- استخدِم ميزة إنشاء مثيلات المواد بشكل صحيح من خلال الاستفادة من
MaterialPropertyBlocks.
الصوت
ابدأ بالتحقّق من الإعدادات الأساسية (الصفحة 41) و تأكَّد من إعدادات استيراد الشبكة المتداخلة هذه:
- أزِل مراجع
AudioClipغير المستخدَمة أو المكرّرة عند استخدام محرّكات صوت تابعة لجهات خارجية، مثل FMOD أو Wwise. - حمِّل البيانات الصوتية مسبقًا. أوقِف ميزة التحميل المسبق للمقاطع التي لا تكون مطلوبة على الفور أثناء وقت التشغيل أو بدء المشهد. يساعد ذلك في تقليل الحمل الزائد للذاكرة أثناء تهيئة المشهد.
الصور المتحركة
- اضبط إعدادات ضغط الصور المتحركة في Unity لتقليل
عدد الإطارات الرئيسية وإزالة البيانات المكرّرة.
- تقليل الإطارات الرئيسية: تتم إزالة الإطارات الرئيسية غير الضرورية تلقائيًا.
- ضغط الرباعيات: يتم ضغط بيانات الدوران لتقليل استخدام الذاكرة.
يمكنك ضبط إعدادات الضغط في إعدادات استيراد الصور المتحركة ضمن علامة التبويب النموذج أو الصور المتحركة.
أعِد استخدام مقاطع الصور المتحركة بدلاً من تكرارها لأشكال مختلفة.
استخدِم عناصر التحكّم في تجاوز Animator لإعادة استخدام عنصر تحكّم Animator واستبدال مقاطع معيّنة بشخصيات مختلفة.
تضمين الصور المتحركة المستندة إلى الفيزياء: إذا كانت صورك المتحركة مستندة إلى الفيزياء أو إجرائية، ضمِّنها في مقاطع الصور المتحركة لتجنُّب العمليات الحسابية في وقت التشغيل.
تحسين نموذج الهيكل العظمي: استخدِم عددًا أقل من العظام في النموذج لتقليل التعقيد واستهلاك الذاكرة.
- تجنَّب استخدام عدد كبير من العظام للأشكال الصغيرة أو الثابتة.
- إذا لم يتم تحريك عظام معيّنة أو لم تكن مطلوبة، أزِلها من النموذج.
قلِّل طول مقطع الصورة المتحركة.
- قُصّ مقاطع الصور المتحركة لتشمل الإطارات الضرورية فقط. تجنَّب تخزين الصور المتحركة غير المستخدَمة أو الطويلة بشكل مفرط.
- استخدِم الصور المتحركة المتكرّرة بدلاً من إنشاء مقاطع طويلة للحركات المتكرّرة.
تأكَّد من إرفاق مكوّن صورة متحركة واحد أو تفعيله فقط. على سبيل المثال، أوقِف مكوّنات الصور المتحركة القديمة أو أزِلها إذا كنت تستخدم Animator.
تجنَّب استخدام Animator إذا لم يكن ضروريًا. بالنسبة إلى المؤثرات المرئية البسيطة، استخدِم مكتبات tweening أو طبِّق التأثير المرئي في نص برمجي. يمكن أن يكون نظام Animator كثيف الاستخدام للموارد، خاصةً على الأجهزة الجوّالة المنخفضة الإمكانات.
استخدِم نظام المهام للصور المتحركة عند التعامل مع عدد كبير من الصور المتحركة، لأنّ هذا النظام تم إعادة تصميمه بالكامل ليكون أكثر كفاءة في استخدام الذاكرة.
المشاهد
عند تحميل مشاهد جديدة، يتم إحضار مواد العرض كعناصر تابعة. ومع ذلك، بدون إدارة مناسبة لدورة حياة مواد العرض، لا يتم تتبُّع هذه العناصر التابعة من خلال عدّادات المراجع. نتيجةً لذلك، قد تبقى مواد العرض في الذاكرة حتى بعد إلغاء تحميل المشاهد غير المستخدَمة، ما يؤدي إلى تجزئة الذاكرة.
- استخدِم ميزة Object Pooling في Unity لإعادة استخدام مثيلات GameObject لعناصر الل101}عب المتكرّرة، لأنّ ميزة Object Pooling تستخدم مكدسًا لتخزين مجموعة من مثيلات الكائنات لإعادة استخدامها، وهي ليست آمنة للاستخدام في سلاسل المحادثات. يؤدي تقليل استخدام
InstantiateوDestroyإلى تحسين أداء وحدة المعالجة المركزية وثبات الذاكرة. - إلغاء تحميل مواد العرض:
- ألغِ تحميل مواد العرض بشكل استراتيجي خلال اللحظات الأقل أهمية، مثل شاشات البداية أو شاشات التحميل.
- يؤدي الاستخدام المتكرّر لـ
Resources.UnloadUnusedAssetsإلى حدوث ارتفاعات في معالجة وحدة المعالجة المركزية بسبب عمليات رصد الاعتماديات الداخلية الكبيرة. - ابحث عن ارتفاعات كبيرة في وحدة المعالجة المركزية في علامة Profiler GC.MarkDependencies.
أزِلها أو قلِّل من معدّل تنفيذها، وألغِ تحميل موارد معيّنة يدويًا بدلاً من ذلك باستخدام Resources.UnloadAsset بدلاً من الاعتماد على الشاملة
Resources.UnloadUnusedAssets().
- أعِد هيكلة المشاهد بدلاً من استخدام Resources.UnloadUnusedAssets باستمرار.
- يمكن أن يؤدي استدعاء
Resources.UnloadUnusedAssets()لـAddressablesإلى إلغاء تحميل الحِزم التي تم تحميلها ديناميكيًا عن غير قصد. أدِر بعناية دورة حياة مواد العرض التي تم تحميلها ديناميكيًا.
متنوعة
التجزئة الناتجة عن عمليات الانتقال بين المشاهد: عند استدعاء الطريقة
Resources.UnloadUnusedAssets()، تنفّذ Unity ما يلي:- تحرير الذاكرة لمواد العرض التي لم تعُد قيد الاستخدام
- تشغيل عملية مشابهة لأداة جمع البيانات المهملة للتحقّق من مجموعة العناصر المُدارة والأصلية بحثًا عن مواد العرض غير المستخدَمة وإلغاء تحميلها
- تنظيف ذاكرة الزخرفة والشبكة ومواد العرض بشرط عدم توفّر أي مرجع نشط
AssetBundleأوAddressable- إنّ إجراء تغييرات في هذه المنطقة أمر معقّد و يتطلب جهدًا جماعيًا من الفريق لتطبيق الاستراتيجيات. ومع ذلك، بعد إتقان هذه الاستراتيجيات، فإنّها تحسّن بشكل كبير استخدام الذاكرة وتقلّل حجم التنزيل وتخفّض تكاليف السحابة الإلكترونية. لمزيد من المعلومات عن إدارة مواد العرض في Unity باستخدام، يُرجى الاطّلاع علىAddressables.العناصر التابعة المشترَكة المركزية: اجمِع العناصر التابعة المشترَكة، مثل التظليلات والزخارف والخطوط، بشكل منهجي في حِزم مخصّصة أو مجموعات
Addressable. يقلّل ذلك من التكرار ويضمن إلغاء تحميل مواد العرض غير الضرورية بكفاءة.استخدِم
Addressablesلتتبُّع الاعتمادية: Addressables تُسهّل ميزة التحميل وإلغاء التحميل، ويمكنها إلغاء تحميل الاعتماديات التي لم يعُد يتم الرجوع إليها تلقائيًا. قد يكون الانتقال إلىAddressablesلإدارة المحتوى وحلّ التبعيات حلاً قابلاً للتطبيق، وذلك حسب حالة اللعبة المحدّدة. حلِّل سلاسل التبعيات باستخدام أداة Analyze tool لتحديد التكرارات أو التبعيات غير الضرورية. بدلاً من ذلك، يُرجى الرجوع إلى Unity Data Tools إذا كنت تستخدم AssetBundles.TypeTrees- إذا تم إنشاءAddressablesوAssetBundlesفي لعبتك ونشرها باستخدام الإصدار نفسه من Unity الذي يستخدمه اللاعب، ولم تكن هناك حاجة إلى التوافق مع الإصدارات السابقة مع إصدارات اللاعب الأخرى، ننصحك بـ إيقاف كتابةTypeTree، ما سيؤدي إلى تقليل حجم الحِزمة ومساحة الذاكرة التي تشغلها كائنات الملفات المتسلسلة. عدِّل عملية التصميم في إعداد حزمة Addressables المحلية ContentBuildFlags إلى DisableWriteTypeTree.
كتابة رمز متوافق مع أداة جمع البيانات المهملة
تستخدم Unity أداة جمع البيانات المهملة (GC) لإدارة الذاكرة من خلال تحديد الذاكرة غير المستخدَمة وتحريرها تلقائيًا. على الرغم من أنّ أداة جمع البيانات المهملة ضرورية، يمكن أن تسبّب مشاكل في الأداء (على سبيل المثال، ارتفاعات في معدّل الإطارات) إذا لم تتم إدارتها بشكل صحيح، لأنّ هذه العملية يمكن أن توقف اللعبة مؤقتًا، ما يؤدي إلى حدوث مشاكل في الأداء وتجربة مستخدم غير مثالية.
يُرجى الرجوع إلى دليل Unity للحصول على تقنيات مفيدة لتقليل معدّل تخصيصات مجموعة العناصر المُدارة، وإلى UnityPerformanceTuningBible، الصفحة 271، للحصول على أمثلة.
تقليل تخصيصات أداة جمع البيانات المهملة:
- تجنَّب استخدام LINQ وlambdas وclosures، التي تخصّص ذاكرة مجموعة العناصر.
- استخدِم
StringBuilderللسلاسل القابلة للتغيير بدلاً من ربط السلاسل. - أعِد استخدام المجموعات من خلال استدعاء
COLLECTIONS.Clear()بدلاً من إعادة إنشاء مثيل لها.
يتوفّر مزيد من المعلومات في الكتاب الإلكتروني Ultimate Guide to Profiling Unity games.
إدارة تحديثات لوحة عرض واجهة المستخدم:
- التغييرات الديناميكية في عناصر واجهة المستخدم: عند تعديل عناصر واجهة المستخدم، مثل خصائص النص أو الصورة أو
RectTransform(على سبيل المثال، تغيير محتوى النص أو تغيير حجم العناصر أو تحريك المواضع)، قد يخصّص المحرّك ذاكرة للكائنات المؤقتة. - تخصيصات السلاسل: غالبًا ما تتطلب عناصر واجهة المستخدم، مثل النص، تعديلات على السلاسل، لأنّ السلاسل غير قابلة للتغيير في معظم لغات البرمجة.
- لوحة العرض غير النظيفة: عند تغيير أي عنصر على لوحة العرض (على سبيل المثال، تغيير الحجم أو تفعيل العناصر وإيقافها أو تعديل خصائص التنسيق)، قد يتم وضع علامة غير نظيفة على لوحة العرض بالكامل أو جزء منها وإعادة إنشائها. يمكن أن يؤدي ذلك إلى تشغيل إنشاء هياكل بيانات مؤقتة (على سبيل المثال، بيانات الشبكة المتداخلة أو مخازن الرؤوس أو عمليات حساب التنسيق)، ما يزيد من إنشاء البيانات المهملة.
- التعديلات المعقدة أو المتكرّرة: إذا كانت لوحة العرض تحتوي على عدد كبير من العناصر أو يتم تعديلها بشكل متكرّر (على سبيل المثال، كل إطار)، يمكن أن تؤدي عمليات إعادة الإنشاء هذه إلى حدوث الاستخدام الزائد للذاكرة.
- التغييرات الديناميكية في عناصر واجهة المستخدم: عند تعديل عناصر واجهة المستخدم، مثل خصائص النص أو الصورة أو
فعِّل ميزة جمع البيانات المهملة التدريجي لتقليل الارتفاعات الكبيرة في مجموعة العناصر من خلال توزيع عمليات تنظيف التخصيصات على إطارات متعدّدة. استخدِم أداة Profiler للتحقّق مما إذا كان هذا الخيار يحسّن أداء لعبتك واستهلاك الذاكرة.
إذا كانت لعبتك تتطلب نهجًا خاضعًا للتحكّم، اضبط وضع جمع البيانات المهملة على يدوي. بعد ذلك، عند تغيير المستوى أو في لحظة أخرى بدون لعب نشط، استدعِ عملية جمع البيانات المهملة.
استدعِ عمليات جمع البيانات المهملة اليدوية GC.Collect() للانتقالات بين حالات اللعبة (على سبيل المثال، تبديل المستوى).
حسِّن المصفوفات بدءًا من ممارسات الرموز البسيطة، وإذا لزم الأمر، استخدِم المصفوفات الأصلية أو الحاويات الأصلية الأخرى للمصفوفات الكبيرة.
راقِب الكائنات المُدارة باستخدام أدوات مثل Unity Memory Profiler لتتبُّع مراجع الكائنات غير المُدارة التي تظل موجودة بعد الإتلاف.
استخدِم علامة Profiler لإرسال البيانات إلى أداة Performance Reporting Tool للحصول على نهج تلقائي.
تجنُّب تسرّبات الذاكرة وتجزئتها
تسرّبات الذاكرة
في رمز C# ، عندما يكون هناك مرجع إلى كائن Unity بعد
إتلاف الكائن، يظل كائن الغلاف المُدار، المعروف باسم Managed
Shell، في الذاكرة. يتم تحرير الذاكرة الأصلية المرتبطة بالمرجع عند إلغاء تحميل المشهد أو عند إتلاف GameObject الذي تم إرفاق الذاكرة به أو أي من الكائنات الرئيسية له من خلال الطريقة Destroy(). ومع ذلك، إذا لم تتم إزالة مراجع أخرى إلى المشهد أو GameObject، قد تظل الذاكرة المُدارة موجودة ككائن Leaked Shell Object. لمزيد من التفاصيل عن كائنات Managed Shell Objects، يُرجى الرجوع إلى
دليل Managed Shell Objects.
بالإضافة إلى ذلك، يمكن أن تحدث تسرّبات الذاكرة بسبب الاشتراكات في الأحداث وlambdas وclosures وعمليات ربط السلاسل والإدارة غير السليمة للكائنات المجمّعة:
- للبدء، يُرجى الاطّلاع على مقالة العثور على تسرّبات الذاكرة لمقارنة لقطات الذاكرة في Unity بشكل صحيح.
- ابحث عن الاشتراكات في الأحداث وتسرّبات الذاكرة. إذا اشتركت الكائنات في الأحداث (على سبيل المثال، من خلال المفوّضين أو UnityEvents) ولكن لم يتم إلغاء اشتراكها بشكل صحيح قبل إتلافها، قد يحتفظ مدير الأحداث أو الناشر بمراجع لتلك الكائنات. يمنع ذلك جمع البيانات المهملة لتلك الكائنات، ما يؤدي إلى تسرّبات الذاكرة.
- راقِب أحداث الفئة العامة أو الفئة الفردية التي لم يتم إلغاء تسجيلها عند إتلاف الكائن. على سبيل المثال، ألغِ الاشتراك أو ألغِ ربط المفوّضين في مدمّرات الكائنات.
- تأكَّد من أنّ إتلاف الكائنات المجمّعة يؤدي إلى إلغاء مراجع مكوّنات الشبكة النصية والزخارف وGameObjects الرئيسية بشكل كامل.
- يُرجى العِلم أنّه عند مقارنة لقطات Unity Memory Profiler وملاحظة ا اختلاف في استهلاك الذاكرة بدون سبب واضح، قد يكون الاختلاف ناتجًا عن برنامج تشغيل الرسومات أو نظام التشغيل نفسه.
تجزئة الذاكرة
تحدث تجزئة الذاكرة عندما يتم تحرير العديد من التخصيصات الصغيرة بترتيب عشوائي. يتم إجراء تخصيصات مجموعة العناصر بالتسلسل، ما يعني إنشاء أجزاء ذاكرة جديدة عندما ينتهي مساحة الجزء السابق. نتيجةً لذلك، لا تملأ الكائنات الجديدة المناطق الفارغة من الأجزاء القديمة، ما يؤدي إلى التجزئة. بالإضافة إلى ذلك، يمكن أن تؤدي التخصيصات المؤقتة الكبيرة إلى تجزئة دائمة طوال مدة جلسة اللعبة.
تكون هذه المشكلة مزعجة بشكل خاص عند إجراء تخصيصات كبيرة قصيرة الأجل بالقرب من تخصيصات طويلة الأجل.
اجمِع التخصيصات استنادًا إلى مدة بقائها، ومن المفترض إجراء التخصيصات طويلة الأجل معًا في وقت مبكر من دورة حياة التطبيق.
المراقبون ومديرو الأحداث
- بالإضافة إلى المشكلة المذكورة في قسم "تسرّبات الذاكرة"، يمكن أن تساهم تسرّبات الذاكرة بمرور الوقت في التجزئة من خلال ترك ذاكرة غير مستخدَمة مخصّصة للكائنات التي لم تعُد قيد الاستخدام.
- تأكَّد من أنّ إتلاف الكائنات المجمّعة يؤدي إلى إلغاء مراجع
مكوّنات الشبكة النصية والزخارف و
GameObjectsالرئيسية بشكل كامل. - غالبًا ما ينشئ مديرو الأحداث ويخزّنون قوائم أو قواميس لإدارة الاشتراكات في الأحداث. إذا كانت هذه القوائم أو القواميس تنمو وتتقلّص ديناميكيًا أثناء وقت التشغيل، يمكن أن تساهم في تجزئة الذاكرة بسبب عمليات التخصيص وإلغاء التخصيص المتكرّرة.
الرمز
- تخصّص الروتينات الفرعية أحيانًا ذاكرة، ويمكن تجنُّب ذلك بسهولة من خلال تخزين عبارة return الخاصة بـ IEnumerator مؤقتًا بدلاً من الإعلان عن عبارة جديدة في كل مرة.
- راقِب باستمرار حالات دورة حياة الكائنات المجمّعة لتجنُّب الاحتفاظ بمراجع شبحية لـ
UnityEngine.Object.
مواد العرض
- استخدِم أنظمة احتياطية ديناميكية لتجارب اللعب المستندة إلى النصوص لتجنُّب التحميل المسبق لجميع الخطوط في الحالات المتعدّدة اللغات.
- نظِّم مواد العرض (على سبيل المثال، الزخارف والجسيمات) معًا حسب النوع ودورة الحياة المتوقّعة.
- اجمِع مواد العرض التي تتضمّن سمات دورة حياة غير نشطة، مثل صور واجهة المستخدم المكرّرة والشبكات الثابتة.
التخصيصات المستندة إلى مدة البقاء
- خصِّص مواد العرض طويلة الأجل في بداية دورة حياة التطبيق لضمان تخصيصات مضغوطة.
- استخدِم NativeCollections أو أدوات تخصيص مخصّصة لـ هياكل البيانات المؤقتة أو الكثيفة الاستخدام للذاكرة (على سبيل المثال، مجموعات الفيزياء).
إجراء الذاكرة المتعلق بالرمز والملفات التنفيذية
يؤثر الملف التنفيذي للعبة والمكوّنات الإضافية أيضًا في استخدام الذاكرة.
البيانات الوصفية لـ IL2CPP
IL2CPP تنشئ بيانات وصفية لكل نوع (على سبيل المثال، الفئات والعناصر العامة و المفوّضون) في مدّة التصميم، ثم يتم استخدامها في وقت التشغيل للانعكاس والتحقّق من النوع و العمليات الأخرى الخاصة بوقت التشغيل. يتم تخزين هذه البيانات الوصفية في الذاكرة ويمكن أن تساهم بشكل كبير في إجمالي استهلاك الذاكرة للتطبيق. يساهم مخزن IL2CPP المؤقت للبيانات الوصفية بشكل كبير في أوقات التهيئة والتحميل. بالإضافة إلى ذلك، لا تزيل IL2CPP تكرار بعض عناصر البيانات الوصفية (على سبيل المثال، الأنواع العامة أو المعلومات المتسلسلة)، ما قد يؤدي إلى زيادة استخدام الذاكرة. يتفاقم ذلك بسبب الاستخدام المتكرّر أو المكرّر للنوع في المشروع.
يمكن تقليل البيانات الوصفية لـ IL2CPP من خلال:
- تجنُّب استخدام واجهات برمجة التطبيقات للانعكاس، لأنّها يمكن أن تساهم بشكل كبير في تخصيصات البيانات الوصفية لـ IL2CPP
- إيقاف الحِزم المُضمّنة
- تطبيق ميزة مشاركة العناصر العامة الكاملة في Unity 2022، ما سيساعد في تقليل الحمل الزائد الناتج عن العناصر العامة. ومع ذلك، للمساعدة في تقليل التخصيصات بشكل أكبر، قلِّل من استخدام العناصر العامة.
إزالة الرمز
بالإضافة إلى تقليل حجم الإصدار، تؤدي إزالة الرمز أيضًا إلى تقليل استخدام الذاكرة. عند الإنشاء باستخدام واجهة IL2CPP الخلفية لكتابة النصوص البرمجية، تؤدي إزالة الرمز الثنائي البايتي المُدار (التي يتم تفعيلها تلقائيًا) إلى إزالة الرمز غير المستخدَم من التجميعات المُدارة. تعمل العملية من خلال تحديد التجميعات الجذرية، ثم استخدام تحليل الرمز الثابت لتحديد الرمز المُدار الآخر الذي تستخدمه تلك التجميعات الجذرية. تتم إزالة أي رمز لا يمكن الوصول إليه. لمزيد من المعلومات عن إزالة الرمز المُدار، يُرجى الاطّلاع على منشور المدونة TTales from the optimization trenches: Better managed code stripping with Unity 2020 LTS ومستندات إزالة الرمز المُدار.
أدوات التخصيص الأصلية
جرِّب أدوات تخصيص الذاكرة الأصلية لضبط أدوات تخصيص الذاكرة بدقة. إذا كانت اللعبة تعاني من نقص في الذاكرة، استخدِم أجزاء ذاكرة أصغر، حتى إذا كان ذلك يتضمّن أدوات تخصيص أبطأ. لمزيد من المعلومات، يُرجى الاطّلاع على مثال أداة تخصيص مجموعة العناصر الديناميكية .
إدارة المكوّنات الإضافية الأصلية وحِزم SDK
العثور على المكوّن الإضافي الذي يسبب المشكلة: أزِل كل مكوّن إضافي وقارِن لقطات ذاكرة اللعبة. يتضمّن ذلك إيقاف الكثير من وظائف الرمز باستخدام Scripting Define Symbols وإعادة تصميم الفئات المرتبطة بشكل كبير بـ الواجهات. يُرجى الاطّلاع على مقالة Level up your code with game programming patterns لتسهيل عملية إيقاف التبعيات الخارجية بدون جعل لعبتك غير قابلة للعب.
التواصل مع مؤلف المكوّن الإضافي أو حزمة SDK: معظم المكوّنات الإضافية ليست مفتوحة المصدر.
إعادة إنتاج استخدام الذاكرة للمكوّن الإضافي: يمكنك كتابة مكوّن إضافي بسيط (استخدِم هذا المكوّن الإضافي من Unity كمرجع) الذي يُجري تخصيصات الذاكرة. افحص لقطات الذاكرة باستخدام Android Studio (لأنّ Unity لا تتتبّع عمليات التخصيص هذه) أو استدعِ الفئة
MemoryInfoوالطريقةRuntime.totalMemory()في المشروع نفسه.
يخصّص مكوّن إضافي من Unity ذاكرة Java والذاكرة الأصلية، وإليك كيفية إجراء ذلك:
Java
byte[] largeObject = new byte[1024 * 1024 * megaBytes];
list.add(largeObject);
الذاكرة الأصلية
char* buffer = new char[megabytes * 1024 * 1024];
// Random data to fill the buffer
for (int i = 1; i < megabytes * 1024 * 1024; ++i) {
buffer[i] = 'A' + (i % 26); // Fill with letters A-Z
}