رجوع
كيف طورنا قالب سباق السرعة في Fortnite من خلال Verse Persistence وغير ذلك
فريق Fortnite
ساعدت إضافة Verse Persistence على تقديم فرصة لترقية جزيرة قالب "تصميم سباق سرعة" في Fortnite Creative إلى UEFN. من تحديثات مثل إضافة السينيمائيات وبناء التضاريس إلى الانتقال من المشغلات نحو Verse، يسرنا مشاركة رحلة تطوير هذا القالب معك. مرحبًا بكم في سباق السرعة مع Verse Persistence!
تم إنشاء قالب "تصميم سباق سرعة" في البداية لاستخدام أصول مضامير السباق المنشأة حديثًا وبناء تجربة سباق مميزة مع بعض ميزات جودة الحياة الرائعة.
من خلال التحديث الأخير، أخذنا نهجًا شموليًا واستبدلنا الكثير من الميزات في الخريطة وقمنا بترقيتها. لنلقِ نظرة على بعض التحديثات التي أجريناها والتي تستفيد كثيرًا من قدرات UEFN الفعالة.
تم استبدال دورة النهار/ الليل القديمة للمشروع الأصلي بإضاءة الفصل الرابع لوضع باتل رويال من Fortnite الأكثر تقدمًا. وقد ساعدتنا هذه الدورة الجديدة على استخدام Lumen، من خلال إنشاء ظلال أنعم وإنارة شاملة واقعية. كما أضفنا ميزات مثل شلال وجعلنا المضمار نفسه أكثر إثارة بصريًا.
قدم Verse Persistence الحل، بتمكين تتبع البيانات عبر جلسات اللعبة. وقد سمحت لنا هذه الوظيفة بمراقبة إحصائيات اللاعب مدى الحياة وإنشاء قوائم متصدرين محلية.
كان هدفنا هو تحديث أفضل زمن دورة اللاعب عند إكمال الدورة وأيضًا تسجيل نقاطه وانتصاراته حين يكمل سباقًا.
كان تحديث أزمنة الدورة بسيطًا، حيث كنا ننتظر LapCompletedEvent في جهاز مدير السباقات ليكشف إكمال اللاعب للدورة. وعندما يبدأ السباق، نشغّل جهاز مؤقت يعمل لكل لاعب. تنتظر دالة WaitForPlayerToFinishLap اللاعب حتى ينهي الدورة، وتحسب زمن الدورة، وتحدّث جدول إحصائياته، ثم تعيد تعيين المؤقت لتسجيل الدورة التالية.
لحل هذه المشكلة، استخدمنا دالة ArraySync. تستدعي ArraySync دالة غير متزامنة لكل عنصر في المصفوفة ممررة إليها وتنتظر حتى اكتمال كل تلك الدوار. بتمرير مصفوفة اللاعبين ودالة WaitForPlayerToFinishRace، أمكننا استدعاء الدالة لكل منهم والانتظار حتى يكمل الجميع السباق.
وقد ساعدنا هذا على منح النقاط وانتصار للاعبين بناءً على مراكزهم حين يكملون السباق، ثم تحديث جدول إحصائياتهم وفقًا لذلك. عندما تنتهي دالة ArraySync، نعلم أن كل اللاعبين أكملوا السباق ويمكنهم إنهاء اللعبة.
ونظرًا لأن لدينا بالفعل إحصائيات مستمرة لكل لاعب، استطعنا استرداد تلك الإحصائيات وعرضها باستخدام لوحات الإعلانات. في منطقة الانتظار قبل اللعبة، أضفنا أجهزة لوحات إعلانات وأجهزة مرجع اللاعب لكل لاعب لإظهار إحصائياته وأزيائه المستخدمة في ذلك الوقت. ولكن ظل فرز هؤلاء اللاعبين بناءً على الأداء تحديًا صعبًا.
ولتحقيق هذا الهدف، نفذنا خوارزمية Merge Sort. تعد Merge Sort خوارزمية فرق تسد شائعة تقسّم المصفوفة بشكل متكرر إلى مصفوفتين، وتفرز كل مصفوفة فرعية، ثم تدمجهما معًا. أضفنا أيضًا القدرة على تمرير دالة مقارنة إلى الخوارزمية، وأنشأ دوال مقارنة لكل إحصائيات اللاعب المختلفة.
متى ما احتجنا لفرز للاعبين، استطعنا استرداد إحصائيات كل لاعب من جدول إحصائياته، وإضافتها جميعًا إلى مصفوفة وتمرير كلاهما ودالة المقارنة إلى خوارزمية Merge Sort. باختيار دالة المقارنة التي مررناها، استطعنا إعادة مصفوفة اللاعبين مفروزة بأي إحصائية. لقد قمنا بتضمين كل من خوارزمية Merge Sort وملف اختبار في القالب الجديد، حتى يمكنك اختبار الخوارزمية بنفسك وتخصيصها وفقًا للدوال لديك!
بعد إعداد آليات الفرز، أنهينا قوائم المتصدرين. خلال الجولة الأولى من اللعبة، ينتشر اللاعبون في منطقة الانتظار قبل اللعبة مع مراجع اللاعب ولوحات الإعلانات. نفرز مراجع اللاعب هذه حسب نقاط مدار الحياة لكل لاعب ونعرض إحصائيات كل منهم على لوحة الإعلانات أمامهم. وهذا يمنح اللاعبين الفرصة لتحليل المنافسة قبل السباق ومعرفة من عليهم مراقبته إذا أرادوا الفوز.
استمروا في اللعب وقد تجدوا أنفسكم أعلى قائمة المتصدرين!

للقيام بهذا، أنشانا متغير Weak Map ثانٍ للاعبين باسم CircuitInfo. يحتفظ متغير CircuitInfo هذا بكل المعلومات التي نحتاج إليها لإعادة التعيين في نهاية اللعبة أو حين يغادر أحد اللاعبين.
استخدمنا متغير Weak Map ثانٍ لتوضيح نوع البيانات المطلوب إعادة تعيينه (معلومات الحلبة) ونوع البيانات المطلوب استمراره للأبد (إحصائيات اللاعب).
نسجل لكل لاعب المعلومات التالية في متغير Weak Map للاعب CircuitInfo:
في دالة OnBegin بجهاز Verse (التي تعمل في بداية كل جولة)، نتعرف على الجولة التي نحن فيها من خلال تكرار كل اللاعبين النشطين والحصول على القيمة الأعلى لآخر جولة مكتملة من بياناتهم المستمرة. نقوم بهذا مرة واحدة لكل جولة ونسجل معلومات الجولة في متغير Weak Map للجلسة حتى يمكن لكل أكواد Verse في المشروع الوصول إلى هذه القيمة في أي وقت دون الحاجة لإعادة حسابها.
حين يكمل اللاعب السباق، بعد اكتشافه من خلال انتظار RaceCompletedEvent في جهاز مدير السباقات، نسجل ترتيب الإنهاء الخاص به وجولته الحالية في متغير Weak Map (CircuitInfo) المقترن باللاعب. نعيد تعيين هذه المعلومات بشرطين:
في القالب المحدّث، نستبدل مشغل النبضات بـ Sequencer للوصول إلى السينيمائيات الافتتاحية. باستخدام Sequencer، تمكنّا من إضافة كاميرات مختلفة وعناصر جهاز عرض فوق الرأس (HUD) وعرض تشكيلة ديناميكية تتعدل حسب عدد اللاعبين النشطين.
على غرار طريقة تكوين مشغل النبضات، نربط الأجهزة بالتسلسل في لحظات مهمة، مما يساعدنا على تحديد وقت عرض نقاط اللاعب التالي أو وقت قطع المقدمة.

نتطلع لتحديث هذا القالب مع إصدار ميزات جديدة لتعزيز تصميمه. أما حاليًا، يمكنك تنزيل القالب واستكشاف مكوناته ودمج وظائفه في مشروعاتك من خلال علامة تبويب القوالب في UEFN. لا يسعنا الانتظار حتى نرى سباقاتكم وقوائم المتصدرين وهي تتشكل!
تم إنشاء قالب "تصميم سباق سرعة" في البداية لاستخدام أصول مضامير السباق المنشأة حديثًا وبناء تجربة سباق مميزة مع بعض ميزات جودة الحياة الرائعة.
من خلال التحديث الأخير، أخذنا نهجًا شموليًا واستبدلنا الكثير من الميزات في الخريطة وقمنا بترقيتها. لنلقِ نظرة على بعض التحديثات التي أجريناها والتي تستفيد كثيرًا من قدرات UEFN الفعالة.
التصميم البصري
ساعدنا وضع التضاريس في UEFN على العودة إلى جذور السباق وإنشاء مضمار طرق وعرة باستخدام الأدوات الجديدة لتحرير التضاريس. تم استبدال الجبال المصنوعة من أصول الصخور في خلفية الجزيرة الأصلية بتصميم تضاريس أكثر طبيعية.تم استبدال دورة النهار/ الليل القديمة للمشروع الأصلي بإضاءة الفصل الرابع لوضع باتل رويال من Fortnite الأكثر تقدمًا. وقد ساعدتنا هذه الدورة الجديدة على استخدام Lumen، من خلال إنشاء ظلال أنعم وإنارة شاملة واقعية. كما أضفنا ميزات مثل شلال وجعلنا المضمار نفسه أكثر إثارة بصريًا.
Verse Persistence: قائمة متصدرين أفضل
في الخريطة الأصلية، استخدم برج ما جهاز مرجع اللاعب لعرض اللاعب في المركز الأول وعدد النقاط التي لديه. لكن البيانات لم تكن ثابتة من جلسة لأخرى.قدم Verse Persistence الحل، بتمكين تتبع البيانات عبر جلسات اللعبة. وقد سمحت لنا هذه الوظيفة بمراقبة إحصائيات اللاعب مدى الحياة وإنشاء قوائم متصدرين محلية.
تتبع إحصائيات اللاعب
طورنا فئة جدول قابلة لإحصائيات اللاعب مستمرة لتتبع انتصارات اللاعب مدى الحياة وأفضل زمن دورة له والنقاط المكتسبة لكل نهاية سباق. استخدمنا أيضًا Weak Map مستمرة باسم PlayerStatsMap لتعيين اللاعبين لجداول إحصائيات اللاعب الخاصة بهم، حتى تستمر إحصائياتهم عبر الجولات والجلسات. ثم أنشأنا فئة مدير إحصائيات لمعالجة تهيئة واسترداد وتحديث هذه الإحصائيات لكل لاعب.كان هدفنا هو تحديث أفضل زمن دورة اللاعب عند إكمال الدورة وأيضًا تسجيل نقاطه وانتصاراته حين يكمل سباقًا.
كان تحديث أزمنة الدورة بسيطًا، حيث كنا ننتظر LapCompletedEvent في جهاز مدير السباقات ليكشف إكمال اللاعب للدورة. وعندما يبدأ السباق، نشغّل جهاز مؤقت يعمل لكل لاعب. تنتظر دالة WaitForPlayerToFinishLap اللاعب حتى ينهي الدورة، وتحسب زمن الدورة، وتحدّث جدول إحصائياته، ثم تعيد تعيين المؤقت لتسجيل الدورة التالية.
تسجيل الانتصارات والنقاط
لقد تبين أن تسجيل الانتصارات والنقاط أكثر تعقيدًا مما كنا نظن. كنا نعلم أنه يمكننا استخدام دالة "WaitForPlayerToFinishRace" الشبيهة لانتظار "RaceCompletedEvent" في مدير السباقات ومعرفة موعد إكمال أي لاعب للسباق. لكننا كنا نريد ألا تنتهي اللعبة إلا حين ينتهي كل اللاعبين، ولم يكن هناك طريقة في مدير السباقات لتتبع هذا أو إنهاء اللعبة حين ينتهون.لحل هذه المشكلة، استخدمنا دالة ArraySync. تستدعي ArraySync دالة غير متزامنة لكل عنصر في المصفوفة ممررة إليها وتنتظر حتى اكتمال كل تلك الدوار. بتمرير مصفوفة اللاعبين ودالة WaitForPlayerToFinishRace، أمكننا استدعاء الدالة لكل منهم والانتظار حتى يكمل الجميع السباق.
وقد ساعدنا هذا على منح النقاط وانتصار للاعبين بناءً على مراكزهم حين يكملون السباق، ثم تحديث جدول إحصائياتهم وفقًا لذلك. عندما تنتهي دالة ArraySync، نعلم أن كل اللاعبين أكملوا السباق ويمكنهم إنهاء اللعبة.
عرض النتائج
لم ينته الأمر عند تسجيل الإحصائيات، فقد احتجنا أيضًا إلى طريقة لعرض تلك الإحصائيات للاعبين. أردنا إنشاء قوائم متصدرين في المستوى تكون ظاهرة، وتفرز اللاعبين حسب نقاطهم على مدار الحياة لإبراز أفضل اللاعبين.ونظرًا لأن لدينا بالفعل إحصائيات مستمرة لكل لاعب، استطعنا استرداد تلك الإحصائيات وعرضها باستخدام لوحات الإعلانات. في منطقة الانتظار قبل اللعبة، أضفنا أجهزة لوحات إعلانات وأجهزة مرجع اللاعب لكل لاعب لإظهار إحصائياته وأزيائه المستخدمة في ذلك الوقت. ولكن ظل فرز هؤلاء اللاعبين بناءً على الأداء تحديًا صعبًا.
ولتحقيق هذا الهدف، نفذنا خوارزمية Merge Sort. تعد Merge Sort خوارزمية فرق تسد شائعة تقسّم المصفوفة بشكل متكرر إلى مصفوفتين، وتفرز كل مصفوفة فرعية، ثم تدمجهما معًا. أضفنا أيضًا القدرة على تمرير دالة مقارنة إلى الخوارزمية، وأنشأ دوال مقارنة لكل إحصائيات اللاعب المختلفة.
متى ما احتجنا لفرز للاعبين، استطعنا استرداد إحصائيات كل لاعب من جدول إحصائياته، وإضافتها جميعًا إلى مصفوفة وتمرير كلاهما ودالة المقارنة إلى خوارزمية Merge Sort. باختيار دالة المقارنة التي مررناها، استطعنا إعادة مصفوفة اللاعبين مفروزة بأي إحصائية. لقد قمنا بتضمين كل من خوارزمية Merge Sort وملف اختبار في القالب الجديد، حتى يمكنك اختبار الخوارزمية بنفسك وتخصيصها وفقًا للدوال لديك!
بعد إعداد آليات الفرز، أنهينا قوائم المتصدرين. خلال الجولة الأولى من اللعبة، ينتشر اللاعبون في منطقة الانتظار قبل اللعبة مع مراجع اللاعب ولوحات الإعلانات. نفرز مراجع اللاعب هذه حسب نقاط مدار الحياة لكل لاعب ونعرض إحصائيات كل منهم على لوحة الإعلانات أمامهم. وهذا يمنح اللاعبين الفرصة لتحليل المنافسة قبل السباق ومعرفة من عليهم مراقبته إذا أرادوا الفوز.
استمروا في اللعب وقد تجدوا أنفسكم أعلى قائمة المتصدرين!

ترتيب المتسابق في خط البداية
يضع إصدار Fortnite Creative من الخريطة اللاعبين بترتيب عشوائي في بداية كل مسابقة. لتحفيز اللاعبين على تحقيق مراكز أعلى في قائمة المتصدرين، حددنا ترتيب خط البداية حسب مركزهم في الجولة السابقة.تتبع معلومات الجولة
بخلاف البيانات القابلة للاستمرار التي استخدمناها لقائمة المتصدرين، احتجنا إلى استمرار معلومات ترتيب المتسابق والحلبة عبر كل الجولات ولكن ليس عبر كل جلسات اللعبة. يقوم متغير Weak Map للجلسات في Verse بإعادة تعيين بياناتها كل جولة، لذا علينا تخزين هذه المعلومات لكل لاعب وإعادة تعيينها بعد انتهاء اللعبة.للقيام بهذا، أنشانا متغير Weak Map ثانٍ للاعبين باسم CircuitInfo. يحتفظ متغير CircuitInfo هذا بكل المعلومات التي نحتاج إليها لإعادة التعيين في نهاية اللعبة أو حين يغادر أحد اللاعبين.
استخدمنا متغير Weak Map ثانٍ لتوضيح نوع البيانات المطلوب إعادة تعيينه (معلومات الحلبة) ونوع البيانات المطلوب استمراره للأبد (إحصائيات اللاعب).
نسجل لكل لاعب المعلومات التالية في متغير Weak Map للاعب CircuitInfo:
- ترتيب الإنهاء
- آخر جولة مكتملة
المنطق الخاص بالجولة
علينا معرفة الجولة التي نحن فيها حتى نطبق منطق خاص بالجولة (مثل ترتيب اللاعبين حسب آخر ترتيب إنهاء إذا لم تكن الجولة الأولى)، كما علينا معرفة وقت إعادة تعيين بيانات اللاعبين. لا توجد حاليًا واجهة برمجة تطبيقات للحصول على الجولة الحالية، ولذا يجب علينا تسجيلها في البيانات القابلة للاستمرار لكل لاعب.في دالة OnBegin بجهاز Verse (التي تعمل في بداية كل جولة)، نتعرف على الجولة التي نحن فيها من خلال تكرار كل اللاعبين النشطين والحصول على القيمة الأعلى لآخر جولة مكتملة من بياناتهم المستمرة. نقوم بهذا مرة واحدة لكل جولة ونسجل معلومات الجولة في متغير Weak Map للجلسة حتى يمكن لكل أكواد Verse في المشروع الوصول إلى هذه القيمة في أي وقت دون الحاجة لإعادة حسابها.
حين يكمل اللاعب السباق، بعد اكتشافه من خلال انتظار RaceCompletedEvent في جهاز مدير السباقات، نسجل ترتيب الإنهاء الخاص به وجولته الحالية في متغير Weak Map (CircuitInfo) المقترن باللاعب. نعيد تعيين هذه المعلومات بشرطين:
- مغادرة اللاعب أثناء اللعبة. نشترك في PlayerRemovedEvent في ساحة اللعب لمعرفة متى يغادر ونستدعي دالة ResetCircuitInfo.
- في بداية كل جولة، نحسب آخر جولة مكتملة. إذا كانت الجولة الأخيرة، نستدعي ResetCircuitInfo لتحديث بيانات اللاعب. نعرف عدد الجولات بالحصول على خاصية قابلة للتحرير على جهاز Verse معيّنة للعدد الإجمالي للجولات لمسابقة محددة. يقع على منشئ الجزيرة مسؤولية التأكد من المطابقة مع إعدادات الجولة.
متى نستخدم خرائط Weak Map للجلسة مقابل خرائط Weak Map للاعب
يعد سباق السرعة الجديد هذه مثالاً رائعًا لإيضاح الفروق ومنطق استخدام متغير خريطة Weak Map للجلسة ومتغير خريطة Weak Map للاعب في أكوادك:- متغيرات خريطة Weak Map للجلسة مفيدة للمجموعات الأحادية وفرز البيانات للجولة الحالية التي لا تريد إعادة حسابها كل مرة.
- متغيرات خريطة Weak Map للاعب مصممة للمعلومات التي يجب أن تستمر عبر جولات وجلسات لعب متعددة لكن يجب أن تقترن بكل لاعب.
مشغل النبضات إلى التسلسل الافتتاحي
في الإصدار الأصلي من قالب سباق السرعة، استعنا بجهاز اسمه مشغل النبضات لتنسيق جزء السباق "اجهز - استعد - انطلق". شغّل مشغل النبضات سلسلة من الأحداث على مدار مدة زمنية محددة من خلال تنشيط المشغلات لعرض النص وتمكين الأضواء في خط البداية.في القالب المحدّث، نستبدل مشغل النبضات بـ Sequencer للوصول إلى السينيمائيات الافتتاحية. باستخدام Sequencer، تمكنّا من إضافة كاميرات مختلفة وعناصر جهاز عرض فوق الرأس (HUD) وعرض تشكيلة ديناميكية تتعدل حسب عدد اللاعبين النشطين.
على غرار طريقة تكوين مشغل النبضات، نربط الأجهزة بالتسلسل في لحظات مهمة، مما يساعدنا على تحديد وقت عرض نقاط اللاعب التالي أو وقت قطع المقدمة.

ما الخطوة التالية؟
مع استمرار UEFN في التطور، نأمل أيضًا في تطوير هذا القالب. هدفنا هو تمكينكم لمتابعة بناء محتوى أكثر تطورًا من خلال UEFN، بالاستفادة من أحدث الميزات لبناء جُزر ممتعة وشيقة.نتطلع لتحديث هذا القالب مع إصدار ميزات جديدة لتعزيز تصميمه. أما حاليًا، يمكنك تنزيل القالب واستكشاف مكوناته ودمج وظائفه في مشروعاتك من خلال علامة تبويب القوالب في UEFN. لا يسعنا الانتظار حتى نرى سباقاتكم وقوائم المتصدرين وهي تتشكل!