تحسين أداء الهواتف المحمولة في Unreal Engine: تشغيل MetaHumans و PCG بمعدل 60 FPS
باختصار
يقدم هذا الدليل التقني حلولاً عملية لتحسين أداء ميزات الجيل التالي مثل MetaHumans و PCG لتشغيلها بسلاسة بمعدل 60 FPS على الهواتف المحمولة باستخدام Unreal Engine. يركز المقال على تقنيات تقليل حمل الرندر عبر التحكم في حدود العظام باستخدام متغيرات DefaultEngine.ini واستبدال خصلات الشعر بـ Hair Cards. كما يوضح كيفية عمل Bake لمخططات PCG إلى HISMs لتخفيف العبء عن CPU في وقت التشغيل، مع الإشارة إلى أهمية استخدام خدمات horizOn لإدارة multiplayer backend بشكل مبسط وفعال.
عائق الهواتف المحمولة: جلب مرئيات الجيل التالي إلى الهواتف
يعمل مشروعك الرائع للـ PC بسلاسة تامة بمعدل 120 FPS على محطة العمل الخاصة بك، ولكن بمجرد اختبار mobile package، ينخفض معدل الإطارات إلى خانة الآحاد، وترتفع درجة حرارة الهاتف المستهدف، ويحدث كراش للـ GPU بسبب skinning buffer overflow. تبدو الميزات المتقدمة مثل MetaHumans، و Procedural Content Generation (PCG)، ومواد Substrate مذهلة على الـ PC وأجهزة الكونسول، ولكنها معروفة بأنها تنهك الهواتف المحمولة. تعمل الـ GPUs والـ CPUs للهواتف المحمولة في حدود حرارية واستهلاك طاقة مقيدة للغاية، حيث يمثل عرض نطاق الذاكرة (memory bandwidth) موردًا ثمينًا للغاية. إن تكييف ميزات الجيل التالي هذه للعمل على الهواتف المحمولة لا يقتصر فقط على تفعيل خانة إعدادات معينة؛ بل يتطلب فهمًا عميقًا ومنهجيًا لحدود bone skinning، وبنية الـ groom، ومسارات عمل procedural baking workflows، وتعقيدات material shading complexities.
تحدي GPU Skinning وحدود العظام (Bone Limits)
عنق زجاجة GPU Skinning على الهواتف المحمولة
يتم تقسيم skeletal meshes إلى chunks من الـ vertices والـ bones قبل إرسالها إلى الـ GPU. يتم معالجة كل chunk في draw call واحدة، وتملك mobile GPUs حداً صارماً من العتاد (hardware limit) لعدد bone matrices التي يمكنها عمل skinning لها في نفس الوقت. يتم تحديد هذا القيد عبر عدد uniform vectors المتاحة للـ vertex shader. يحتوي الهيكل العظمي الافتراضي لشخصية MetaHuman على أكثر من 600 bone، وهو ما يتجاوز بسهولة حدود الهواتف المحمولة ويؤدي إلى حدوث rendering errors، أو vertex tearing، أو تجمد الـ GPU بالكامل.
لتجاوز هذا القيد المفروض من العتاد، يجب إجبار المحرك على تقسيم chunks الـ skeletal mesh بحيث لا تشير أي draw call واحدة إلى أكثر من عدد معين من العظام. يتم تحقيق ذلك من خلال ضبط إعدادات توافق الـ skinning. إذا لم تقم بتطبيق هذا التكوين، فستفشل نماذج الشخصيات في عمل skinning بشكل صحيح على أجهزة Android و iOS، مما يعرض مجسمات ثابتة أو مشوهة للغاية.
تكوين ملف DefaultEngine.ini
لحسم حدود bone skinning، يجب عليك تعديل ملف تكوين مشروعك DefaultEngine.ini. ابحث عن هذا الملف في مجلد Config الموجود في جذر مشروعك. وتحت قسم [ConsoleVariables]، أضف السطر التالي:
[ConsoleVariables]
Compat.MAX_GPUSKIN_BONES=75
يوجه متغير الكونسول (console variable) هذا مترجم الـ shader للحد من أقصى عدد من العظام لكل skinning chunk إلى 75. هذا هو الحد الصارم للعتاد (hardware limit) لـ GPUs الهواتف المحمولة القديمة أو متوسطة الفئة. ضع في اعتبارك أن تعيين هذه القيمة إلى رقم أقل يجبر skeletal mesh compiler على تقسيم الـ mesh إلى عدد أكبر من الـ chunks. في حين أن هذا يحل مشكلة التوافق مع الرندر، إلا أن زيادة عدد الـ chunks تعني زيادة عدد الـ draw calls، مما قد ينقل عنق زجاجة الأداء إلى الـ CPU render thread الخاص بك.
تقليل عدد العظام وتجريد المكونات (Component Stripping)
بالنسبة لشخصيات الخلفية أو الشخصيات غير القابلة للعب (NPCs) التي لا تتطلب تعبيرات وجه كاملة، يجب عليك تجريد الهيكل العظمي (skeleton). على سبيل المثال، يمكن أن يؤدي إزالة الأصابع وأصابع القدم وعظام تعبيرات الوجه إلى تقليل إجمالي عدد عظام الشخصية من أكثر من 600 إلى أقل من 75. وهذا يسمح برندر mesh الشخصية كـ single chunk دون أي تضخم في الـ draw calls.
إذا كنت تقوم أيضًا بنشر dedicated servers للعبتك الـ multiplayer، فإن تحسينات جانب العميل (client-side optimizations) ليست سوى نصف القصة. ستحتاج أيضًا إلى تحسين أداء جانب السيرفر (server-side performance) عن طريق تجريد أصول الرندر (rendering assets) تمامًا. تحقق من دليلنا خطوة بخطوة حول how to master Unreal Engine dedicated server asset stripping لتقليل استهلاك الذاكرة على السيرفر وتحسين أداء الـ CPU.
تحسين شعر MetaHuman: مقارنة بين Groom Strands و Hair Cards
تكلفة رندر Strand
تقوم تقنية رندر groom strand من Epic برسم خصلات الشعر الفردية بشكل ديناميكي. وعلى الرغم من أن هذا ينتج شعرًا مفصلاً للغاية على GPUs الأجهزة المكتبية المتطورة، إلا أنه مكلف للغاية. يعتمد رندر الـ strand على ممرات الـ compute shader لعمل ترتيب للعمق (depth sorting) وتوليد خرائط الظل (shadow map generation)، مما يستهلك معدل تعبئة بكسل (pixel fill rate) وعرض نطاق ذاكرة (memory bandwidth) هائلين.
على الهواتف المحمولة، يكون رندر الـ strand إما غير مدعوم تمامًا أو يعمل بتكلفة غير مقبولة—حيث يستهلك غالباً أكثر من 15 مللي ثانية من وقت الـ GPU لمجرد شعر شخصية واحدة. تفتقر GPUs الهواتف المحمولة إلى عرض نطاق الذاكرة (memory bandwidth) المطلوب لترتيب وتظليل مئات الآلاف من خصلات الشعر الفردية في كل إطار.
تطبيق Hair Cards
الحل يكمن في استبدال groom strand بـ hair cards. تمثل الـ hair cards الشعر باستخدام polygon meshes مسطحة ومبسطة مطبقة عليها pre-rendered hair textures. يتوافق هذا الأسلوب بشكل كبير مع مسار الرندر الأمامي للهواتف المحمولة (mobile forward render path).
لتطبيق الـ hair cards، افتح MetaHuman Creator وتأكد من إنشاء LODs الخاصة بالـ card-based groom لشخصيتك. يؤدي استبدال الشعر المعتمد على الـ strands بـ card-based grooms إلى تقليل وقت الرندر لشعر شخصية واحدة من حوالي 18.2 مللي ثانية إلى 0.9 مللي ثانية على شريحة هاتف حديثة مثل Apple A15 أو Snapdragon 8 Gen 1. يتيح لك هذا التوفير الهائل تخصيص ميزانية الرندر لعناصر أسلوب اللعب (gameplay) أو تفاصيل البيئة.
تعطيل Post-Process Anim Blueprints
تستخدم شخصيات MetaHumans ملفات post-process animation blueprints لتوجيه حركة العظام الثانوية، وأشكال العضلات التصحيحية، وديناميكيات المفاصل. على الرغم من أن هذا يضيف حركة جلد واقعية على الـ PC، إلا أنه يجري حسابات هيكلية معقدة على الـ CPU في كل إطار. وعلى الهواتف المحمولة، يمكن أن يتسبب هذا العبء على الـ CPU بسهولة في حدوث عنق زجاجة لخيط اللعبة (game thread).
يمكنك تعطيل post-process animation blueprint على الهواتف المحمولة لاستعادة دورات الـ CPU الضائعة. يتم ذلك عن طريق تعيين bDisablePostProcessAnims = true في مكونات الـ skeletal mesh. يوفر تعطيل هذه الـ post-processes ما يصل إلى 4.5 مللي ثانية من وقت إطار الـ CPU على عتاد الهواتف المحمولة القياسي.
تحسين PCG لبيئات الهواتف المحمولة
العبء الإضافي لتشغيل PCG في وقت التشغيل (Runtime)
يتيح لك إطار عمل Procedural Content Generation (PCG) ملء بيئاتك ديناميكيًا عن طريق نشر الـ static meshes والـ foliage والـ actors بناءً على قواعد وأحجام محددة. ومع ذلك، فإن تشغيل مخططات (graphs) الـ PCG في وقت التشغيل (runtime) على CPUs الهواتف المحمولة يسبب مشاكل أداء حادة (performance hitches). يمكن لتوليد المخطط في وقت التشغيل أن يجمد خيط اللعبة (game thread) لمدة تتراوح بين 1.5 إلى 3 ثوانٍ أثناء تحميل المراحل (level loading) أو توليد اللاعبين (player spawning).
في ألعاب الـ multiplayer، يعد هذا التوقف مؤشرًا خطيرًا؛ حيث يمكن أن يؤدي إلى فقدان الحزم (packet loss) ويتسبب في إلغاء مزامنة الحالة بين العميل والسيرفر (client-server state desynchronization). للحفاظ على أداء عالٍ، يجب عليك عمل pre-bake لمخططات الـ PCG الخاصة بك داخل الـ editor. يقوم هذا بتحويل النسخ الإجرائية (procedural instances) إلى مجسمات ثابتة (static geometry) قبل عمل packaging للعبتك.
خطوات عمل Bake لمخطط الـ PCG
- تحديد PCG Volume: في منفذ العرض لـ Unreal Editor، حدد حجم الـ PCG (PCG volume) الذي يحتوي على عناصر البيئة الخاصة بك.
- التوليد والمعاينة (Generate and Inspect): في details panel الخاصة بالـ volume، انقر على Generate لمعاينة التوزيع الإجرائي لأصولك.
- التصدير إلى Actor (Export to Actor): ابحث عن خيار Export to Actor داخل قائمة أدوات الـ PCG المساعدة.
- اختيار Instanced Meshes: حدد Hierarchical Instanced Static Mesh (HISM) كصيغة مستهدفة. هذه المجموعة من الـ instances محسنة للغاية لـ GPUs الهواتف المحمولة، لأنها ترسم جميع المجسمات المتطابقة في draw call واحدة للـ GPU.
- مسح المخطط (Clear the Graph): بعد التصدير، اضبط محفز التوليد لـ PCG volume على Editor Only أو قم بمسح الـ volume. يمنع هذا محرك اللعبة في وقت التشغيل (runtime engine) من محاولة إعادة إنشاء المخطط.
عملية الحجب الديناميكي (Dynamic Culling) والـ Streaming للـ HISM
بمجرد عمل bake لنسخ الـ PCG الخاصة بك إلى HISMs، يجب عليك تكوين مسافات الحجب (culling distances) الخاصة بها. تدعم الـ HISMs ميزة الـ culling لكل نسخة بشكل مستقل (per-instance culling)، مما يعني أن الـ instances التي تقع خارج مسافة معينة من الكاميرا لن يتم رسمها. اضبط قيمتي Start Cull Distance و End Cull Distance في details panel لمكونات الـ HISM الخاصة بك. بالنسبة للهواتف المحمولة، يوصى بمسافة culling تتراوح بين 5000 و 8000 وحدة للحفاظ على إجمالي عدد المضلعات المرئية (visible polygon count) ضمن ميزانية GPU الهاتف المحمول.
سكربت تحسين C++ للمشاريع الفردية أو التجارية
لأتمتة هذه التحسينات في وقت التشغيل (runtime)، يمكنك كتابة فئة مساعدة مخصصة (custom helper class). يوضح كود C++ التالي كيفية التحقق من المنصة المستهدفة، وفرض مستويات تفاصيل منخفضة (LODs) برمجياً، وتعطيل post-process animation blueprints، وإجبار مكونات الـ groom على استخدام رندر معتمد على الـ cards عند توليد شخصية MetaHuman على جهاز محمول. يمكنك تطبيق ذلك في فئة مثل UMetaHumanMobileOptimizer:
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Components/SkeletalMeshComponent.h"
#include "GroomComponent.h"
#include "MetaHumanMobileOptimizer.generated.h"
UCLASS()
class MYPROJECT_API UMetaHumanMobileOptimizer : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "Optimization")
static void OptimizeMetaHumanForMobile(AActor* MetaHumanActor)
{
if (!MetaHumanActor)
{
return;
}
// Apply optimizations exclusively on Android and iOS platforms
#if PLATFORM_ANDROID || PLATFORM_IOS
TArray<USkeletalMeshComponent*> SkeletalComponents;
MetaHumanActor->GetComponents<USkeletalMeshComponent>(SkeletalComponents);
for (USkeletalMeshComponent* MeshComp : SkeletalComponents)
{
if (MeshComp)
{
// Force a low LOD (LOD 3 or 4) to bypass dense meshes
MeshComp->SetMinLOD(3);
MeshComp->SetForcedLOD(3);
// Disable expensive post-process animation blueprints
MeshComp->bDisablePostProcessAnims = true;
// Adjust animation tick rate to only calculate when visible
MeshComp->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
// Strip physics asset to avoid CPU collision overhead on cosmetic joints
MeshComp->SetPhysicsAsset(nullptr);
}
}
TArray<UGroomComponent*> GroomComponents;
MetaHumanActor->GetComponents<UGroomComponent>(GroomComponents);
for (UGroomComponent* GroomComp : GroomComponents)
{
if (GroomComp)
{
// Force the groom to use card rendering instead of strands
GroomComp->SetUseCards(true);
}
}
#endif
}
};
يمكن استدعاء هذه الوظيفة المساعدة (helper function) من حدث BeginPlay لشخصيتك أو مباشرة بعد توليد (spawning) الـ actor الخاص بـ MetaHuman. باستخدام ماكرو التجميع الشرطي (PLATFORM_ANDROID || PLATFORM_IOS)، يقوم المترجم بإزالة هذه التجاوزات من نسخ الـ PC وأجهزة الكونسول، مما يسمح لك بالحفاظ على الدقة المرئية عبر المنصات المختلفة تلقائيًا.
تكييف مواد Substrate لتناسب GPUs الهواتف المحمولة
ما هو Substrate؟
يستبدل Substrate نموذج التظليل التقليدي (shading model) في Unreal Engine بإطار عمل مواد معياري متعدد الطبقات. يسمح Substrate للمطورين بدمج طبقات تظليل متعددة (على سبيل المثال، وضع طبقة glossy clear coat مباشرة فوق طبقة rough metal). في حين أن Substrate ممتاز لأصول السينمائية المتطورة، إلا أنه يمثل عقبة أداء خطيرة لمصيرات (renderers) الهواتف المحمولة.
تعتمد GPUs الهواتف المحمولة بشكل كبير على بنيات الرندر القائمة على المربعات (tiled rendering architectures) حيث يعد ناقل الذاكرة (memory bus) بين نواة الـ GPU وذاكرة الإطارات المؤقتة المدمجة (on-chip frame buffer) بمثابة عنق زجاجة رئيسي. تزيد مواد Substrate المعقدة من عدد البايتات لكل بكسل (BPP - bytes per pixel) المكتوبة في الـ frame buffer، مما يتسبب في اختناق حراري (thermal throttling) وانخفاض في معدل الإطارات.
إدارة تعقيد المواد عبر مفاتيح جودة المواد (Quality Level Switches)
للحفاظ على أداء مواد Substrate على الهواتف المحمولة، يجب عليك استخدام عقد Material Quality Level Switch و Feature Level Switch داخل محرر المواد (Material Editor). تتيح لك هذه العقد تبسيط مخطط المواد (material graph) بناءً على المنصة المستهدفة.
بالنسبة لمنصات الهواتف المحمولة، قم بتبسيط المخطط عن طريق تجاوز مزيج Substrate متعدد الطبقات. بدلاً من ذلك، ارجع إلى شريحة واحدة (single slab) تقارب المظهر المرئي لأصلك. من خلال توجيه عقد المواد الخاصة بك عبر مفتاح الجودة (quality switch)، يمكنك تقليل عرض نطاق الكتابة من 32 بايت لكل بكسل إلى 8 بايت لكل بكسل كمعيار قياسي، مما ينتج عنه جهاز يعمل بدرجة حرارة أقل ومعدلات إطارات مستقرة.
5 ممارسات عملية أساسية لتحسين أداء الهواتف المحمولة
- عمل Bake مسبق لجميع مخططات PCG إلى HISMs: لا تقم بتشغيل مخططات PCG على جانب العميل في وقت التشغيل. قم بعمل bake للمخططات إلى Hierarchical Instanced Static Meshes (HISMs) أثناء التطوير في الـ editor وقم بتكوين مسافات culling للبدء والنهاية المناسبة.
- تقييد عدد العظام عالمياً: أضف
Compat.MAX_GPUSKIN_BONES=75إلى ملف مشروعك DefaultEngine.ini لضمان رندر skeletal meshes بشكل صحيح على GPUs الهواتف المحمولة دون التسبب في حدوث overflow للذاكرة المؤقتة (buffer overflow). - استخدام card-based grooms حصرياً: قم بتعطيل grooms القائمة على الـ strands لملفات تعريف الهواتف المحمولة (mobile profiles). يقلل رندر الشعر القائم على الـ cards من وقت إطار الـ GPU من 18 مللي ثانية إلى أقل من 1 مللي ثانية لكل شخصية.
- الاستفادة من مفاتيح جودة المواد (Material Quality Switches): قم بتطبيق عقدة Material Quality Level Switch في مواد Substrate لتبسيط طبقات التظليل المعقدة إلى شريحة واحدة (single slab) على منصات الهواتف المحمولة، مما يقلل من عرض نطاق الـ GPU.
- تعطيل post-process animation blueprints: قم بتعيين
bDisablePostProcessAnims = trueعلى مكونات skeletal mesh لشخصيتك على الهواتف المحمولة لاستعادة دورات الـ CPU الثمينة على خيط اللعبة (game thread).
معادلة اللعب الجماعي (Multiplayer) والـ Backend
ما بعد الرندر: تحسين شبكة الهواتف المحمولة
عند تطوير ألعاب multiplayer عابرة للمنصات، لا تمثل تحسينات جانب العميل سوى جزء واحد من المعادلة. تواجه الهواتف المحمولة تقلبات متكررة في الشبكة، والتبديل بين بيانات الهاتف (5G/4G) وشبكات الواي فاي (Wi-Fi). تقدم هذه التقلبات فقدان الحزم (packet loss)، والارتجاف (jitter)، وارتفاعات مفاجئة في زمن الاستجابة (latency spikes).
إذا لم يكن كود مزامنة الشبكة قويًا، فقد تؤدي هذه الارتفاعات في زمن الاستجابة إلى حدوث the Unreal Engine multiplayer sync bug، حيث تخرج مزامنة الـ actor replication عن المزامنة وتدمر حالة العالم (world state). تعد إدارة حالات الـ multiplayer تحت قيود شبكة الهواتف المحمولة مشكلة معقدة تتطلب resilient network drivers، وضغط الفروق (delta compression)، ومصالحة السيرفر الموثوق (server-authoritative reconciliation).
تفويض البنية التحتية باستخدام horizOn
إن بناء وصيانة backend مرن للـ multiplayer بنفسك يتطلب جهدًا هندسيًا هائلاً. ستحتاج إلى توفير موزع الأحمال (load balancers)، وإدارة global database replication، وتطبيق منطق الـ matchmaking، والتعامل مع فواتير الهاتف المحمول. يمكن أن يستغرق عمل البنية التحتية هذا شهورًا من وقت التطوير ويتطلب صيانة مستمرة.
مع horizOn، تأتي خدمات الـ backend هذه مهيأة مسبقًا. توفر horizOn لمطوري الألعاب ميزات مثل session matchmaking، ومزامنة الحالة منخفضة زمن الاستجابة (low-latency state synchronization)، وحفظ قواعد البيانات (database persistence)، والمصادقة عابرة المنصات (cross-platform authentication) مباشرة دون أي إعداد إضافي. يتيح لك هذا التركيز على تحسين تطبيقات العملاء (clients) وصقل حلقة اللعبة (game loop)، بدلاً من إدارة مجموعات السيرفرات (server clusters) وتوسيع نطاق قاعدة البيانات.
الخاتمة والخطوات التالية
يتطلب تحسين ميزات الجيل التالي مثل MetaHumans و PCG للهواتف المحمولة رقابة صارمة على ميزانيات الرندر، وتجميع skeletal mesh، وتعقيد تظليل المواد. من خلال عمل bake المسبق لأصولك الإجرائية، وتقييد حدود عدد العظام، واستخدام رندر الشعر القائم على الـ cards، يمكنك تقديم تجارب عابرة للمنصات عالية الدقة على الأجهزة المحمولة.
هل أنت مستعد لتوسيع نطاق الـ multiplayer backend الخاص بك؟ جرب horizOn مجانًا أو تحقق من API docs لمعرفة كيف يمكنك تبسيط بنيتك التحتية والحفاظ على مزامنة لاعبيك عبر الـ PC والكونسول والهواتف المحمولة.
المصدر: Tutorial: Optimizing Next Gen Features for Mobile Game Development