أفضل ألعاب الـ roguelike لا تكتفي بقتلك فحسب، بل تجعلك ترغب في الموت.
قد يبدو هذا غريبًا، ولكن أي شخص خسر محاولة في منتصف الليل وبدأ أخرى في الساعة 12:03 يعرف هذا الشعور. الموت مؤلم. تنخفض صحتك إلى الصفر. تمتلئ الشاشة بالفشل. ومع ذلك، تجد إصبعك يحوم بالفعل فوق زر التشغيل. هناك شيء ما في العشرين دقيقة الماضية كان مهماً بما يكفي لدرجة أنك لا تستطيع ترك اللعبة حيث انتهت.
هذه هي حلقة "محاولة واحدة أخرى" (one more run loop)، وهي ليست محض صدفة، بل هي مصممة بعناية. إذا كنت تبني لعبة roguelike أو أي لعبة تعتمد على الموت الدائم (permadeath)، فإن مهمتك بالكامل هي الموازنة بين قوتين متضادتين. يجب أن يخسر اللاعب بما يكفي ليشعر بالتوتر، ويجب أن يحتفظ بما يكفي ليشعر بالأمل.
عملة الفشل
في مشروعي الأخير، Neon Survivor، أردت تحقيق ذلك التوتر تحديدًا. عندما يموت اللاعب، يختفي كل شيء تم جمعه خلال المحاولة باستثناء شيء واحد: الذهب. يتم إيداع هذا الذهب تلقائيًا. وعند العودة إلى القائمة، ينفقه اللاعب على ترقيات دائمة. ثم يعاود اللعب، وهو أقوى قليلاً مما كان عليه من قبل.
هذه الحلقة البسيطة هي ما يحرك اللعبة بأكملها. بدونها، يكون الموت نقطة نهاية. يبتعد اللاعب لأن محاولته الأخيرة لم تمنحه شيئًا. أما معها، فيصبح الموت مجرد فاصلة. تحولت المحاولة إلى رحلة لجمع الموارد (farming trip). الخسارة مؤلمة، لكنها دفعت ثمن الغد أيضًا.
الخدعة بسيطة. أنت تحتفظ بشيء ما حتى عندما تفقد كل شيء آخر. الصعوبة تكمن في التأكد من أن الشيء الذي تحتفظ به له قيمة دون تدمير التحدي. إذا كانت الترقيات ضعيفة للغاية، سيتوقف اللاعب عن الاهتمام. وإذا كانت قوية للغاية، فستلعب اللعبة نفسها. وفي كلتا الحالتين، تنهار الحلقة.
ساعتان، وصفر من الارتباك
لبناء هذا بشكل صحيح، كان عليّ إدارة خطين زمنيين منفصلين.
ساعة المحاولة (Run Clock) تُعاد ضبطها في كل مرة تضغط فيها على زر التشغيل. وهي تتبع الصحة، والنتيجة الحالية، وعدد موجات الأعداء، وأي تعزيزات مؤقتة (power-ups) تم جمعها خلال الجلسة. عندما تموت الشخصية، تعود هذه الساعة إلى الصفر.
أما ساعة الميتا (Meta Clock) فلا تُعاد ضبطها أبدًا. فهي تحتفظ بإجمالي الذهب المكتسب عبر كل محاولة، وأعلى موجة تم الوصول إليها، وكل ترقية دائمة تم شراؤها. تستمر هذه الساعة في العمل بغض النظر عن عدد مرات تحديث المتصفح.
امزج بين هذين الاثنين وستحصل على أخطاء (bugs) يصعب تتبعها ومن المؤلم إصلاحها. لقد رأيت مطورين يمسحون تقدم اللاعب عن طريق الخطأ أثناء إعادة ضبط مشهد روتيني لأن دالة التنظيف (cleanup function) لمست مخزن بيانات خاطئ. تتبخر بيانات Meta Clock، ويعود اللاعب إلى صفر ذهب وصفر ترقيات. عند تلك النقطة، تنكسر العلاقة بينك وبين لاعبك. هم لا يبدأون محاولة جديدة؛ بل يبدأون ضغينة جديدة.
الحفاظ على فصل الساعات ليس مجرد خيار أسلوبي، بل هو استراتيجية للبقاء.
كيف يتعامل Phaser v4 مع هذا الفصل
لقد بنيت Neon Survivor باستخدام Phaser v4، الذي يوفر أداتين محددتين لهذه المشكلة.
يحتفظ الـ Registry بالبيانات الحية في الذاكرة للجلسة الحالية. إنه سريع، وبسيط، ولكنه يتبخر في اللحظة التي يقوم فيها اللاعب بتحديث الصفحة.
يقوم LocalStorage بحفظ البيانات في المتصفح نفسه. وهو يصمد أمام إغلاق علامات التبويب، وإعادة تشغيل المتصفح، وانقطاع التيار الكهربائي. ولكنه أيضًا أبطأ وأقل موثوقية؛ إذ يمكن للمتصفحات حظره، أو تقييده، أو مسحه إذا امتلأت حصص التخزين.
كان خياري التصميمي صارمًا. الـ Registry هو المصدر الوحيد للحقيقة أثناء اللعب. تقرأ اللعبة منه، وتكتب فيه، وتثق به تمامًا. أما LocalStorage فلا يعمل كمؤلف مشارك، بل يعمل كمرآة.
إليك كيف يعمل التدفق: تكتب اللعبة عملية شراء ترقية في الـ Registry. ويقوم فئة مدير (manager class) واحدة بمراقبة الـ Registry. وعندما يكون ذلك مناسبًا، تقوم هذه الفئة بعكس بيانات الـ Registry في الـ LocalStorage. إذا حظر المتصفح عملية الكتابة، فلا تتعثر اللعبة. وإذا فشل التخزين، تظل الجلسة الحالية تعمل بشكل مثالي. قد يفقد اللاعب تقدمه فقط إذا أغلق علامة التبويب في نفس الثانية تمامًا، لكن الجلسة نفسها لا تتعطل أبدًا.
يمنع هذا النمط وقوع كارثة خفية. إذا سمحت لكل نظام بالكتابة مباشرة في LocalStorage، فستنشئ تبعيات على واجهة برمجة تطبيقات (API) هشة. اللاعب الذي رفع إعدادات الخصوصية إلى أقصى حد أو الجهاز الذي يعاني من نقص في مساحة التخزين قد يرى اللعبة تتباطأ أو تتجمد أثناء القتال لأن إحدى وظائف الخلفية حاولت حفظ الإحصائيات. من خلال جعل الـ Registry هو المصدر الوحيد للحقيقة، تحافظ على سرعة الحركة وتحتوي المخاطر.
اترك الكود يتنفس
استخدمت أيضًا الأحداث (events) لفصل الأنظمة عن بعضها البعض. عندما تنتهي المحاولة، لا تتولى GameScene مسؤولية جنازتها الخاصة؛ فهي لا تستدعي دالة حفظ، ولا تستورد أداة تخزين، بل تكتفي ببساطة بإرسال حدث "run-ended" مع حمولة (payload) من البيانات ذات الصلة.
يتولى مستمع (listener) منفصل عمليات الحساب. فهو يستقبل الحدث، ويحدث الـ Meta Clock، ويخبر المدير (manager) بنقل الإجماليات الجديدة إلى الـ LocalStorage.
هذا الفصل يؤتي ثماره على الفور. يمكنني إعادة كتابة الـ GameScene بالكامل، أو استبدال شخصية اللاعب، أو تغيير زاوية الكاميرا، أو حتى تغيير نوع اللعبة من survival إلى bullet hell دون المساس بنظام الحفظ. الأنظمة مستقلة؛ فهي تتواصل عبر الأحداث (events)، وليس عبر استدعاءات مباشرة للدوال (function calls). وهذا يعني تعارضات دمج (merge conflicts) أقل، وأخطاءً برمجية أقل، وكوداً برمجياً لا يتحول إلى "سباغيتي" متشابك بعد ستة أشهر.
ترقيات تغير قواعد اللعبة
امتلاك عمود فقري تقني لا فائدة منه إذا كانت المكافآت تبدو وكأنها مجرد جدول بيانات. لقد قضيت الكثير من الوقت في دراسة كيف يكون الشعور الفعلي للترقيات أثناء اللعب.
بعض الترقيات آمنة؛ مثل زيادة سرعة الحركة، أو مكافأة الصحة، أو سرعة إعادة التلقيم. هذه الترقيات تمنح اللاعب مساحة أكبر للخطأ، وهي مريحة، حيث تقلص صعوبة اللعبة دون تغيير قواعدها.
أما الترقيات الأخرى، فهي تعيد كتابة القواعد بالكامل. في لعبة Neon Survivor، أضفت ميزة "الطلقات المخترقة" (Piercing Rounds). قبل هذه الترقية، كانت الرصاصة تتوقف عند أول عدو تصطدم به، أما بعد الترقية، فهي تخترق الأعداء، مما قد يؤدي إلى إبادة صفوف كاملة بطلقة واحدة.
الفرق هنا درامي. قد تمنحك السرعة والصحة قدرة على البقاء لفترة أطول، لكن "الطلقات المخترقة" تغير طريقة تموضعك في الملعب. ستبدأ في اصطفاف الأعداء، وستتوقف عن المناورة (kiting) حول الحواف لتبدأ في الاختراق من المنتصف. هكذا تتوسع مساحة اتخاذ القرار في اللعبة.
يجب أن يغير التقدم الجيد قرارات اللاعب، لا أن يكتفي بزيادة أرقامه فقط. إذا كانت كل ترقية مجرد زيادة مئوية، فسيتوقف اللاعب عن قراءة الأوصاف؛ سينقر، ويرقي، ثم ينسى. أما إذا جعلته الترقية يعيد التفكير في استراتيجيته، فسيتذكرها، وسيتحدث عنها، وسيعود ليرى ما الذي قد يقلب موازين اللعبة مرة أخرى.
الثمرة الحقيقية
حلقة "جولة واحدة أخرى" (one more run) ليست نظاماً واحداً، بل هي علاقة بين الخسارة والمكسب، مبنية على هندسة برمجية نظيفة ومكافآت ذات معنى.
ابنِ خطين زمنيين متميزين، واحفظ الـ Meta Clock وكأنه يحمل ثقة لاعبيك، لأنه يفعل ذلك حقاً. استخدم أدوات محركك للحفاظ على سرعة البيانات الحية (live data) وأمان البيانات المستمرة (persistent data). افصل المشاهد (scenes) عن وحدات التخزين الخاصة بك لتتمكن من التطوير المستمر دون خوف. وعندما تصمم الترقيات، اسأل نفسك: هل تمنح اللاعب مزيداً من الوقت، أم مزيداً من الخيارات المثيرة للاهتمام؟
إذا أتقنت هذا، فلن يكتفي لاعبوك بتحمل الموت، بل سيعتمدون عليه. ستصبح كل جولة بمثابة دفعة مقدمة للجولة التالية، ولن تعود اللعبة مجرد سلسلة من عمليات إعادة التشغيل، بل ستصبح صعوداً واحداً مستمراً.
