الانتقال إلى المحتوى الرئيسي
يُنشأ اختبار اللعب، والميزات التي يشغّلها، ونموذج ملاحظاته في صفحة الاختبار في لوحة تحكم Protokite (البداية)؛ وهذه الصفحة تشرح ما يفعله البناء بها. لا يعمل أيٌّ مما يلي إلا إذا شغّله الاختبار في Protokite وسمح به اللاعب، ولكلٍّ منه عقد Blueprint آمنة في كل بناء: فمع عدم وجود اختبار لعب قائم تجيب بـfalse أو بفراغ أو بـTurned Off ولا تغيّر شيئًا (عدا Protokite Get Playtest Settings التي لا تحتاج إلى اختبار). أما إعداد البناء نفسه ففي صفحة إضافة Unreal.

من Blueprint أو من C++

كل عقدة من عقد اختبار اللعب في هذه الصفحة موجودة في فئة Protokite | Playtest في قائمة إجراءات Blueprint، ولا تحتاج إلى أكثر من تفعيل الإضافة. وكل عقدة تستدلّ على اختبار اللعب من الرسم الذي استدعاها. وتستدعي C++ الدوالّ نفسها على UProtokitePlaytestLibrary، وتمرّر أي كائن في عالم اللعبة (الممثّل أو الواجهة التي تُجري الاستدعاء، أي this) حيث تستدلّ العقدة على الاختبار بنفسها. أضف وحدة الإضافة إلى ملف Build.cs الخاص بوحدة لعبتك، وضمّن ترويستها:
هذا السطر يجعل الإضافة الاختيارية مطلوبة. فلا تعود وحدة لعبتك تُبنى دون أن تكون Protokite Playtest مثبّتة ومفعّلة، فتبقى الإضافة في كل بناء للمشروع، ومنها أبنية الإصدار. وإن أردت أن تبقى اختيارية، أو كانت لعبتك قائمة على Blueprint فقط، فاستخدم العقد: فهي لا تحتاج إلى سطر في Build.cs.

ما الذي سمح به اللاعب

يسأل البناء لاعبه عمّا يجوز لاختبار اللعب أن يجمعه قبل أن يجمع شيئًا، وإجابته هي التي تقرّر ما تفعله بقية هذه الصفحة: وتجيب Protokite Is Playtest Feature Enabled عن الأمرين معًا أصلًا: فميزة يشغّلها الاختبار ويستثنيها اللاعب تُقرأ مُطفأة في كل موضع. وتعطي Protokite Get Playtest Consent الإجابة نفسها، أما السؤال وكيف تطرحه لعبتك بشاشاتها والإعداد الذي يوقفه ففي صفحة إضافة Unreal.
ولا يدخل نموذج الملاحظات في تلك الإجابة. فهو لا يُرسل إلا حين يملؤه اللاعب ويضغط «إرسال»، وذلك فعله هو في الحالين — ونموذجٌ تعذّر على تشغيل سابق إرساله يُرسل لاحقًا مهما كانت إجابته بعد ذلك.

تسجيل الفيديو

تُسجَّل شاشة اللعبة منذ اللحظة التي يُحمّل فيها الاختبار، بتسجيل واحد لكل تشغيل، في ملف WebM يشغّله المتصفّح. ولا يُسجَّل الوقت الذي تكون فيه اللعبة في الخلفية. وتضبط إعدادات Video Recording في Protokite Playtest Settings المقاس ومعدّل الإطارات ومعدّل البتّ — ‏1280×720 و30 إطارًا في الثانية وVideo Bitrate (kbps) بقيمة 2000 افتراضيًا — وحدّين أيهما جاء أولًا: Recording Length Limit (Minutes) بقيمة 60، وRecording Size Limit (MB) بقيمة 1536. ويُرفع التسجيل إلى جلسته:
  • حين ينهيه حدّ الطول أو حدّ الحجم؛
  • حين تطلب اللعبة ذلك، عبر Protokite Stop And Upload Playtest Recording أو زر Upload your recording في نموذج الملاحظات؛
  • حين توقفه اللعبة نهائيًا، عبر Protokite Stop Video Recording؛
  • وفي بداية تشغيل لاحق، لكل ما لم يستطع تشغيل سابق إرساله: رفع فاشل، أو خروج، أو انهيار.
الخروج من اللعبة لا يرفع. فلا يمكن إرسال تسجيل كامل أثناء الإغلاق، ولذلك يُحفظ ويرسله التشغيل التالي. ويُحذف التسجيل بعد رفعه، كما يحذف التشغيل التالي تسجيلًا لم تبدأ له جلسة اختبار قط، إذ لا سبيل إلى إرساله أبدًا.
وتنتظر التسجيلات تحت Saved/ProtokitePlaytest/Recordings/، ضمن Recordings Disk Budget (MB) بقيمة 4096. وحين تضيق المساحة تُحذف أقدم مقاطع الاختبار أولًا، وتُحذف التسجيلات المنتظرة للرفع أخيرًا. ولا تعرض على اللاعب طريقةً لإرسال تسجيله إلا ما دامت Protokite Can Send Playtest Recording صحيحة. فهي كاذبة حين لا تكون جلسة قد بدأت للتسجيل، والضغط على زر حينها سيوقف التسجيل ولن يرسل شيئًا.

إخبار اللاعب بأن تسجيله أُرسل

اربط On Recording Upload Finished لتخبر اللاعب بما جرى. يُطلق مرة واحدة حين ينتهي تسجيل التشغيل، ومعه قيمتان:
  • Uploaded صحيحة حين يصل التسجيل إلى جلسته.
  • Why Not تقول لماذا لم يُرفع، بما في ذلك تعذّر بدء الرفع من أصله (لا جلسة Protokite يُرسل إليها، أو حزمة Flock لا تعمل). والتسجيل الذي لم يُرفع يُحفظ لتشغيل لاحق، ما لم تكن جلسة لم تبدأ له قط.
ولا يُطلق أثناء إغلاق اللعبة، ولا لتسجيلات تركها تشغيل سابق، ولا لمقاطع الاختبار. والحدث تابع لـProtokite Playtest Subsystem، الذي يعيش على نسخة اللعبة (game instance). وفي Blueprint ابحث في قائمة الإجراءات عن Get ProtokitePlaytestSubsystem (تحت GameInstance Subsystems)؛ وعنوان العقدة في الرسم هو Protokite Playtest Subsystem. اسحب من مخرجها واربط الحدث من هناك:
يشغّل Event BeginPlay العقدة Bind Event to On Recording Upload Finished، وهدفها عقدة Protokite Playtest Subsystem، مربوطةً بحدث مخصّص Recording Upload Finished فيه Uploaded وWhy Not؛ يتفرّع على Uploaded، ويطبع فرع False القيمة Why Not

عند BeginPlay، اربط On Recording Upload Finished واطبع Why Not حين تكون Uploaded كاذبة.

وفي C++ يجب أن يكون المعالج UFUNCTION()، لأن الحدث مندوب ديناميكي (dynamic delegate) (الوحدة والترويسة في من Blueprint أو من C++):

التحليلات الثقيلة

ترسل الإضافة ما يلي عبر تحليلات حزمة Flock، ضمن فئة playtest، وتُقرأ في Dashboards → Game Metrics:
  • ‏performance_window لكل عشر ثوانٍ من اللعب: وسيط زمن الإطار والنسبتان المئويتان 95 و99 منه، والتلعثمات، والذاكرة المستخدَمة وذروتها؛
  • ‏level_loaded لكل مرحلة يحمّلها نسخة اللعبة هذه، ومعها المرحلة السابقة، ومدة التحميل إن كان تحميلًا حاجزًا؛
  • وأحداثك أنت، عبر Protokite Record Playtest Event.

تسجيل حدث من أحداثك

تأخذ Protokite Record Playtest Event اسم حدث وخصائصه. ابدأ الخصائص بـMake Flock Event Properties (لا تحتاج إلى أي توصيل)، ثم أضف عقدة Flock Event Property لكل خاصية (Integer أو String أو Float أو Boolean أو String Array)، كلٌّ منها تغذّي التالية، ووصّل الأخيرة بـProperties:
حدث مخصّص Boss Defeated يمرّ إلى Protokite Record Playtest Event بالاسم boss_defeated وبخصائص مبنية بـMake Flock Event Properties وFlock Event Property (Integer) للمفتاح seconds بالقيمة 95

حين يسقط الزعيم، سجّل boss_defeated ومعه عدد الثواني التي استغرقها القتال.

وفي C++ (الوحدة والترويسة في من Blueprint أو من C++):
وتعيد false، ولا تسجّل شيئًا، حين:
  • لا يكون اختبار لعب قائمًا، أو يُبقي التحليلات الثقيلة مُطفأة (أو استثنى اللاعب بيانات اللعب)؛
  • يكون الاسم مما ترسله الإضافة بنفسها، performance_window أو level_loaded؛
  • ترفض حزمة Flock الحدث: تحليلاتها مُطفأة، أو الموافقة محجوبة، أو الاسم فارغ، أو أطول من 200 حرف.
ولا يُرسل شيء ما دام Analytics Enabled في حزمة Flock مُطفأً؛ ويقول السجل ذلك مرة واحدة. وانظر التحليلات والأحداث لتعرف كيف تتكامل الفئة مع لوحات المعلومات.

الاستثناءات

تبقى الاستثناءات من شأن حزمة Flock. فهي تلتقط أسطر الأخطاء، وفشل التحققات، وأخطاء Blueprint مثل Accessed None، والانهيارات، بإعداداتها هي تحت Analytics | Exceptions، وتبلّغ عنها في Diagnostics → Errors. ولا يشغّل مفتاح الاستثناءات في اختبار اللعب ذلك ولا يطفئه؛ وحين يطلب الاختبار الاستثناءات ويكون Analytics Capture Exceptions مُطفأً، يحذّر السجل مرة واحدة عند بدء الجلسة. وللتحقّق من وصول الاستثناءات إلى لوحة التحكم، اكتب Flock.RaiseTestException error أو Flock.RaiseTestException blueprint 100 في طرفية بناء Development. فمئة حالة من العطل نفسه تصل بلاغًا واحدًا، ثم بلاغ تكرار واحدًا يعدّ التسعة والتسعين الباقية بعد أن تُغلق نافذة التكرار.

نموذج الملاحظات

تُكتب أسئلة النموذج في صفحة Feedback form الخاصة بالاختبار في Protokite. وحين ينشر الاختبار نموذجًا، يفتحه اللاعبون بمفتاح Feedback Form Key (‏F9 افتراضيًا؛ واضبطه على none لتعطيل المفتاح)، أو تفتحه لعبتك عبر Protokite Open Feedback Form — من قائمة الإيقاف المؤقت مثلًا. وتحقّق من Protokite Can Open Feedback Form أولًا: فهي كاذبة حين لا ينشر الاختبار نموذجًا، فتستطيع لعبتك إخفاء مدخلها الخاص بدل أن تعرض ما لا وجود له. وفي قائمة إيقاف مؤقت مبنية على هيئة Widget Blueprint، ضع هذه العقد على حدث On Clicked لزر «أرسل ملاحظاتك» فيها؛ فالحدث المخصّص في الصورة يقوم مقام ذلك الحدث.
حدث مخصّص Open Feedback From Pause Menu يمرّ إلى Protokite Can Open Feedback Form ثم Branch، وعند True إلى Protokite Open Feedback Form

مدخل «أرسل ملاحظاتك» في قائمة الإيقاف المؤقت: لا تفتح النموذج إلا حين يوجد.

وفي C++ (الوحدة والترويسة في من Blueprint أو من C++):
وتغلقه Protokite Close Feedback Form دون إرسال، وتقول Protokite Is Feedback Form Open هل هو معروض.
نموذج ملاحظات فوق اللعبة، فيه تقييم واختيار وإجابات نصية وزر «ارفع تسجيلك»

نموذج منشور، مبني من اختبار اللعب: كل أسئلته، والزر الذي يرسل التسجيل.

وما دام النموذج مفتوحًا يظهر المؤشر وتذهب الكتابة إليه؛ وإغلاقه أو إرساله يعيد الأمرين إلى ما كانا عليه. وPause The Game While The Form Is Open مُطفأ افتراضيًا، لأن الإيقاف المؤقت لا يفعل شيئًا في لعبة متعددة اللاعبين ويضيّع ما كان لاعب فردي على وشك وصفه. ويمكن أن تظهر صورك أنت على النموذج أيضًا. اضبطها في Project Settings → Plugins → Protokite Playtest Settings تحت Feedback Form، أو من Blueprint عبر Protokite Get Playtest Settings: كل إعداد منها يُقرأ في كل مرة يُفتح فيها النموذج. لاستيراد صورة للنموذج:
  1. استوردها على هيئة texture، مثلًا بسحبها إلى Content Browser. وتناسب لوحة النموذج الداكنة الصورُ ذات الخلفية الشفافة.
  2. افتح الـ texture واضبط Padding and Resizing على None. وإلا مُدّت إلى قوة للعدد 2 فظهرت باهتة، أو حُشيت فظهرت مضغوطة فوق شريط من الحشو.
  3. أبقِ خرائط mip فيها، لتبقى ناعمة حين تُرسم أصغر: Mip Gen Settings على أي قيمة غير NoMipmaps، وغير FromTextureGroup إن كانت Texture Group هي UI.
  4. احفظها، واخترها في Feedback Form Logos أو Feedback Form Footer Logo.
وكل سؤال يأتي من الاختبار، فتحرير النموذج في Protokite يغيّر ما يراه اللاعبون بلا بناء جديد. وتُفحص الإجابات قبل إرسالها، مع عرض كل مشكلة عند سؤالها. والنموذج الذي يتعذّر إرساله يُحفظ ويرسله تشغيل لاحق، وإرساله مرة أخرى في الجلسة نفسها يحلّ محلّ الإجابات السابقة.

نموذج من عندك

لترسم الأسئلة في واجهتك أنت بدل النموذج المدمج، اقرأ النموذج، وسجّل إجابات اللاعب، وافحصها، ثم أرسلها:
  1. اقرأ النموذج. تعيد Protokite Get Feedback Form القيمة false، مع نموذج فارغ، حين لا ينشر الاختبار نموذجًا. ويحمل Form العنوان والوصف وFields، حقلًا لكل سؤال. ولكل حقل Id، وType (نوعه)، وLabel، وHelp Text، وOptions (لسؤال الاختيار)، وRequired.
  2. اختر عنصر التحكّم بحسب نوع السؤال. فكّك الحقل، وقارن Type فيه بـ Equal Exactly (String) مع عقدة Protokite Feedback Question Kind لكل نوع (Text وText Area وRating وSelect وCheckbox) بدل كتابة النوع بيدك: فالأنواع تتطابق حرفًا بحرف. وعامِل نوعًا لا تعرفه معاملة النص، فهكذا يقرؤه الخادم.
  3. سجّل كل إجابة تحت Id سؤالها بالعقدة الخاصة بنوعه. واترك Answers في العقدة الأولى دون توصيل، كما في الصورة: فهي تبدأ من غير إجابات. هذه العقد الأربع لا منافذ تنفيذ لها: تعيد كل منها قيمة Answers جديدة مضافًا إليها تلك الإجابة، وتترك Answers التي أُعطيت لها دون تغيير. فمرّر مخرج Answers من كل عقدة إلى مدخل Answers في التي تليها، واحفظ المخرج الأخير في متغيّر (من نوع Protokite Playtest Form Answers). والإجابة التي لا يُحفظ مخرجها تضيع.
  4. افحص الإجابات بـFind Feedback Form Problems ‏(Answers وForm). فهي تسرد كل سؤال سيرفض الخادم الإجابات بسببه، ولكلٍّ منه Field Id وMessage تعرضها للاعب بجوار ذلك السؤال. والقائمة الفارغة تعني أنها ستُقبل.
  5. أرسلها بـProtokite Send Feedback Form Answers. وتعيد false، مع تحذير في السجل، حين لا يكون للاختبار نموذج أو لا يكون هناك من تُرسل الإجابات باسمه. والإجابات التي يتعذّر إرسالها الآن تُحفظ على القرص ويرسلها تشغيل لاحق.
النجاح هو القيمة المعادة true: فالإجابات محفوظة على القرص وفي طريقها. ثم يقول السجل، تحت LogProtokitePlaytest، ‏Sending the feedback form.، وحين يقبلها Protokite ‏The feedback form was sent. (أو The feedback form is kept until the Flock SDK is running. حين لا تكون حزمة Flock قد بدأت). أما الرفض فيسجّل The feedback form was not sent مع سبب Protokite، والسؤال الذي رفضه حين يسمّيه، وطريقة الإصلاح. في الصورة، fun وcomments هما Id سؤالين في نموذج الاختبار: سؤال تقييم وسؤال نصي. استخدم Id كل سؤال في نموذجك أنت.
حدث مخصّص Send My Feedback يمرّ إلى Protokite Get Feedback Form ثم Branch؛ وSet Feedback Rating Answer للسؤال fun بالقيمة 4 تغذّي Set Feedback Text Answer للسؤال comments؛ وتُحفظ النتيجة في المتغيّر Answers؛ ثم Find Feedback Form Problems وLength و== 0 وBranch تقود إلى Protokite Send Feedback Form Answers

قيّم المتعة بـ4، وأضف تعليقًا، واحفظ النتيجة في Answers، ولا ترسلها إلا حين لا ينقصها شيء.

والمثال نفسه في C++ (الوحدة والترويسة في من Blueprint أو من C++). وفي C++ تُسجَّل الإجابات على قيمة FProtokitePlaytestFormAnswers واحدة في مكانها، ويُفحص نوع الحقل بـIsOfKind وأسماء ProtokitePlaytestFormFieldTypes:
والمعرّفات fun وcomments وwould_play_again وplatform تمثّل أسئلة نموذجك أنت: استخدم المعرّفات (Id) التي يسلّمك إياها النموذج.
قاعدتان يسهل إغفالهما. اضبط كل مربع اختيار، مؤشَّرًا كان أو غير مؤشَّر: فالخادم يعدّ المربع غير المؤشَّر إجابةً، ومربعًا مطلوبًا لم يُضبط قط يُعدّ ناقصًا. والخيار المختار يجب أن يطابق حرفًا بحرف — فالخادم يقارن الخيارات تمامًا، وcrash ليست Crash.

عقد Blueprint

جميعها تحت Protokite | Playtest. وكل عقدة تستدلّ على اختبار اللعب من الرسم الذي استدعاها. وكل الأسماء تبدأ بـ”Protokite” عدا عقد الإجابات: Set Feedback … Answer وSet Feedback Chosen Option وFind Feedback Form Problems.

تجريبه في المحرّر

قبل وجود أي اختبار لعب، أشّر على Record Video In Play In Editor في Project Settings → Plugins → Protokite Playtest Local Settings واضغط Play، أو اكتب ProtokitePlaytest.RecordTestVideo 30 في طرفية بناء Development. وتُحفظ مقاطع الاختبار تحت Saved/ProtokitePlaytest/Recordings/TestVideos/ ولا تُرفع أبدًا. وأمام اختبار لعب حقيقي، اضغط Play بالإعدادات المذكورة في صفحة الإعداد وسجّل الدخول بـ Flock.LoginWithDevice. ثم، في طرفية بناء Development: وأوقات الانتظار موجودة لأن كل أمر في -ExecCmds يُنفَّذ في الإطار الأول، قبل أن يكون قد سُجِّل شيء أو بدأت جلسة.

فحص بناء اختبار اللعب

يفحص ProtokitePlaytest.SelfTest كل ما يفعله بناء اختبار اللعب أمام Protokite الحقيقي دفعةً واحدة، ويسجّل سطرًا لكل خطوة وعدًّا في النهاية: Playtest self-test finished: 16 passed, 0 failed, 1 skipped. وأبنية Development فقط. ويقف كل فحص إلى جوار طلب يجب أن يرفضه Protokite: مفتاح واجهة خاطئ، ومفتاح مفقود، وإصدار لا اختبار لعب مرتبط به، وبدء جلسة لا يسمّي لاعبًا، ونموذج تنقصه إجابة مطلوبة أو يختار خيارًا ليس في القائمة، ونموذج ورابط رفع ونهاية جلسة لجلسة غير موجودة. فالفحص الذي ينجح دائمًا لا يميّز بناءً سليمًا من بناء معطوب، ولذلك لا يمرّ الرفض إلا بحالة HTTP الخاصة به، ومع النموذج بالسؤال الذي يسمّيه. وهو لا يسجّل دخول أحد، فسجّل الدخول أولًا. ومن سكربت:
  • ينهي جلسة التشغيل بوصفها خطوته الأخيرة، فشغّله في تشغيل مستقل. وأي جلسة كان ينبغي لرفضٍ أن يمنعها تُنهى فورًا، فلا يترك الفحص شيئًا مفتوحًا.
  • يترك أثرًا في لوحاتك: نموذجًا مملوءًا على جلسته، وحدث playtest_self_test واحدًا، وعطل Blueprint واحدًا أُثير مرتين ليُعدّ تكراره، وتسجيل التشغيل.
  • وتُتخطّى خطوة، مع ذكر السبب، حين لا يكون أحد قد أجاب عمّا يجوز لاختبار اللعب جمعه (أجب أولًا بـ ProtokitePlaytest.AnswerConsent video_and_play_data)؛ وحين لا يشغّل الاختبار ميزتها؛ وحين لا يكون في البناء مُرمِّز فيديو، وهو كل منصة عدا Windows بنواة 64 بت؛ وحين لا يستطيع التشغيل الرسم (‏-nullrhi)، إذ لا يُسجَّل شيء؛ وحين يكون Analytics Cache Failed Events مُطفأً، إذ لا يمكن مراقبة بلاغ يُرسل لحظة إنشائه؛ ولاختبار مغلق ما لم تسمِّ واحدًا.

ما الذي يبقى على جهاز اللاعب