بہترین roguelikes صرف آپ کو مارتے نہیں ہیں۔ وہ آپ کو مرنے کی خواہش دلاتے ہیں۔
یہ سننے میں عجیب لگتا ہے، لیکن جس نے بھی آدھی رات کو ایک رن (run) ہارا ہو اور 12:03 پر دوسرا شروع کیا ہو، وہ اس احساس کو جانتا ہے۔ موت تکلیف دیتی ہے۔ آپ کی صحت صفر ہو جاتی ہے۔ اسکرین ناکامی سے بھر جاتی ہے۔ پھر بھی آپ کی انگلی پہلے سے ہی 'play' بٹن پر موجود ہوتی ہے۔ گزشتہ بیس منٹ میں کچھ ایسا تھا جو اتنا اہم تھا کہ آپ گیم کو وہیں چھوڑ کر نہیں جا سکتے جہاں وہ ختم ہوئی تھی۔
یہ "ایک اور رن" (one more run) کا لوپ ہے، اور یہ اتفاقی نہیں ہے۔ اسے ڈیزائن کیا گیا ہے۔ اگر آپ کوئی roguelike یا 'permadeath' والا کوئی بھی گیم بنا رہے ہیں، تو آپ کا پورا کام دو متضاد قوتوں میں توازن برقرار رکھنا ہے۔ کھلاڑی کو اتنا ہارنا چاہیے کہ وہ تناؤ محسوس کرے، اور اتنا بچانا چاہیے کہ اسے امید برقرار رہے۔
ناکامی کی کرنسی
اپنے تازہ ترین پروجیکٹ، Neon Survivor میں، میں بالکل اسی تناؤ کو چاہتا تھا۔ جب کھلاڑی مرتا ہے، تو رن کے دوران جمع کی گئی ہر چیز غائب ہو جاتی ہے سوائے ایک چیز کے: گولڈ (gold)۔ وہ گولڈ خود بخود بینک میں جمع ہو جاتا ہے۔ مینو میں واپس آ کر، وہ اسے مستقل اپ گریڈز (permanent upgrades) پر خرچ کرتے ہیں۔ پھر وہ دوبارہ گیم میں کود پڑتے ہیں، پہلے سے تھوڑے زیادہ طاقتور ہو کر۔
یہ سادہ سا لوپ پورے گیم کو سہارا دیتا ہے۔ اس کے بغیر، موت ایک 'فل اسٹاپ' (full stop) ہے۔ کھلاڑی گیم چھوڑ دیتا ہے کیونکہ پچھلے رن سے اسے کچھ حاصل نہیں ہوا۔ اس کے ساتھ، موت ایک 'کوما' (comma) ہے۔ رن ایک 'فارمنگ ٹرپ' (farming trip) بن جاتا ہے۔ نقصان تکلیف دہ ہوتا ہے، لیکن یہ کل کے لیے ادائیگی بھی کرتا ہے۔
چال سادہ ہے۔ آپ کچھ نہ کچھ اپنے پاس رکھتے ہیں، چاہے آپ باقی سب کچھ کھو دیں۔ مشکل یہ یقینی بنانا ہے کہ جو چیز آپ اپنے پاس رکھتے ہیں وہ چیلنج کو ختم کیے بغیر اہمیت رکھتی ہو۔ اگر اپ گریڈز بہت کمزور ہوں گے، تو کھلاڑی کو پرواہ نہیں رہے گی۔ اگر وہ بہت زیادہ طاقتور ہوں گے، تو گیم خود بخود چلنے لگے گا۔ دونوں صورتوں میں لوپ ٹوٹ جاتا ہے۔
دو گھڑیاں، صفر الجھن
اسے صحیح طریقے سے بنانے کے لیے، مجھے دو الگ الگ ٹائم لائنز (timelines) کو سنبھالنا پڑا۔
Run Clock ہر بار 'play' دبانے پر ری سیٹ ہو جاتا ہے۔ یہ صحت، موجودہ اسکور، دشمنوں کی لہروں (enemy waves) کی تعداد، اور سیشن کے دوران حاصل کردہ کسی بھی عارضی پاور اپس (power-ups) کا حساب رکھتا ہے۔ جب کردار مرتا ہے، تو یہ گھڑی واپس صفر پر آ جاتی ہے۔
Meta Clock کبھی ری سیٹ نہیں ہوتا۔ یہ ہر کوشش کے دوران کمائے گئے کل گولڈ، پہنچے گئے سب سے زیادہ ویو (wave)، اور خریدے گئے ہر مستقل اپ گریڈ کا ریکارڈ رکھتا ہے۔ براؤزر کتنی ہی بار ریفریش ہو جائے، یہ گھڑی چلتی رہتی ہے۔
ان دونوں کو ملا دیں تو ایسے بگ (bugs) پیدا ہوتے ہیں جنہیں ٹریک کرنا مشکل اور ٹھیک کرنا تکلیف دہ ہوتا ہے۔ میں نے ڈویلپرز کو دیکھا ہے کہ وہ معمول کے سین ری سیٹ (scene reset) کے دوران غلطی سے کھلاڑی کی پیشرفت (progress) مٹا دیتے ہیں کیونکہ ایک کلین اپ فنکشن نے غلط ڈیٹا اسٹور کو چھو لیا تھا۔ Meta Clock کا ڈیٹا غائب ہو جاتا ہے۔ کھلاڑی صفر گولڈ اور صفر اپ گریڈز پر واپس آ جاتا ہے۔ اس موڑ پر، آپ کا اور آپ کے کھلاڑی کا رشتہ ٹوٹ جاتا ہے۔ وہ ایک نیا رن شروع نہیں کر رہے ہوتے؛ بلکہ وہ ایک نئی دشمنی شروع کر رہے ہوتے ہیں۔
گھڑیوں کو الگ رکھنا محض ایک اسٹائل کا انتخاب نہیں ہے۔ یہ بقا کی حکمت عملی ہے۔
Phaser v4 اس تقسیم کو کیسے سنبھالتا ہے
میں نے Neon Survivor Phaser v4 میں بنایا ہے، جو اس مسئلے کے لیے دو مخصوص ٹولز فراہم کرتا ہے۔
Registry موجودہ سیشن کے لیے میموری میں لائیو ڈیٹا رکھتا ہے۔ یہ تیز ہے۔ یہ سادہ ہے۔ یہ اس لمحے بھی غائب ہو جاتا ہے جب کھلاڑی پیج ریفریش کرتا ہے۔
LocalStorage ڈیٹا کو خود براؤزر میں محفوظ کرتا ہے۔ یہ ٹیب بند ہونے، براؤزر ری اسٹارٹ ہونے اور بجلی جانے کے باوجود محفوظ رہتا ہے۔ یہ سست اور کم قابل بھروسہ بھی ہے۔ اگر اسٹوریج کوٹہ بھر جائے تو براؤزرز اسے بلاک، تھروٹل (throttle) یا صاف کر سکتے ہیں۔
میرا ڈیزائن کا انتخاب سخت تھا۔ گیم پلے کے دوران Registry ہی حقیقت کا واحد ذریعہ (source of truth) ہے۔ گیم اس سے پڑھتا ہے، اس میں لکھتا ہے، اور اس پر مکمل بھروسہ کرتا ہے۔ LocalStorage ایک شریک مصنف کے طور پر کام نہیں کرتا۔ یہ ایک آئینے کے طور پر کام کرتا ہے۔
یہ رہا کام کرنے کا طریقہ۔ گیم اپ گریڈ کی خریداری کا ڈیٹا Registry میں لکھتا ہے۔ ایک سنگل مینیجر کلاس Registry پر نظر رکھتی ہے۔ ضرورت پڑنے پر، وہ مینیجر Registry کے ڈیٹا کو LocalStorage میں عکس (mirror) کر دیتا ہے۔ اگر براؤزر لکھنے سے روک دے، تو گیم میں کوئی رکاوٹ (stutter) نہیں آتی۔ اگر اسٹوریج ناکام ہو جائے، تب بھی موجودہ سیشن بالکل ٹھیک چلتا ہے۔ کھلاڑی صرف اسی صورت میں اپنی پیشرفت کھو سکتا ہے اگر وہ بالکل اسی سیکنڈ میں ٹیب بند کر دے، لیکن سیشن خود کبھی کریش نہیں ہوتا۔
یہ پیٹرن ایک خاموش تباہی سے بچاتا ہے۔ اگر آپ ہر سسٹم کو براہ راست LocalStorage میں لکھنے کی اجازت دیتے ہیں، تو آپ ایک کمزور API پر انحصار پیدا کر دیتے ہیں۔ پرائیویسی سیٹنگز زیادہ رکھنے والے کھلاڑی یا کم اسٹوریج والے ڈیوائس پر گیم لڑائی کے دوران سست ہو سکتا ہے یا جم (freeze) سکتا ہے کیونکہ کوئی بیک گراؤنڈ فنکشن اسٹیٹس (stats) محفوظ کرنے کی کوشش کر رہا ہے۔ Registry کو حقیقت کا واحد ذریعہ بنا کر، آپ ایکشن کو تیز اور خطرے کو محدود رکھتے ہیں۔
کوڈ کو سانس لینے دیں
میں نے سسٹم کو الگ کرنے (decouple) کے لیے ایونٹس (events) کا بھی استعمال کیا۔ جب ایک رن ختم ہوتا ہے، تو GameScene اپنے جنازے کا خود انتظام نہیں کرتا۔ یہ کسی 'save' فنکشن کو کال نہیں کرتا۔ یہ کسی اسٹوریج یوٹیلیٹی کو امپورٹ نہیں کرتا۔ یہ صرف متعلقہ ڈیٹا کے ساتھ ایک "run-ended" ایونٹ جاری (emit) کرتا ہے۔
ایک الگ لسنر حساب کتاب سنبھالتا ہے۔ یہ ایونٹ وصول کرتا ہے، Meta Clock کو اپ ڈیٹ کرتا ہے، اور منیجر کو نئے ٹوٹل کو LocalStorage میں منتقل کرنے کا حکم دیتا ہے۔
یہ علیحدگی فوری طور پر فائدہ مند ثابت ہوتی ہے۔ میں سیو سسٹم کو چھوئے بغیر مکمل GameScene کو دوبارہ لکھ سکتا ہوں، پلیئر کریکٹر کو تبدیل کر سکتا ہوں، کیمرہ اینگل بدل سکتا ہوں، یا یہاں تک کہ صنف کو survival سے bullet hell میں بھی منتقل کر سکتا ہوں۔ یہ سسٹمز ایک دوسرے سے آزاد ہیں۔ وہ براہ راست فنکشن کالز کے بجائے ایونٹس کے ذریعے بات چیت کرتے ہیں۔ اس کا مطلب ہے کم مرج کنفلیکٹس، کم بگز، اور ایک ایسا کوڈ بیس جو چھ ماہ بعد 'اسپیگیٹی' (انتہائی الجھا ہوا کوڈ) نہ بن جائے۔
وہ اپ گریڈز جو گیم کو بدل دیتے ہیں
اگر انعامات کسی اسپریڈ شیٹ کی طرح محسوس ہوں تو ایک مضبوط تکنیکی ڈھانچہ ہونا بے کار ہے۔ میں نے اس بات پر بہت وقت صرف کیا کہ اپ گریڈز کو کھیلنے کا تجربہ حقیقت میں کیسا محسوس ہوتا ہے۔
کچھ اپ گریڈز محفوظ ہوتے ہیں۔ اضافی حرکت کی رفتار۔ بونس ہیلتھ۔ تیز ری لوڈ۔ یہ کھلاڑی کو غلطی کرنے کے لیے زیادہ گنجائش دیتے ہیں۔ یہ تسلی بخش ہوتے ہیں۔ یہ گیم کے قواعد بدلے بغیر اسے آسان بنا دیتے ہیں۔
دیگر اپ گریڈز قواعد کو مکمل طور پر بدل دیتے ہیں۔ Neon Survivor میں، میں نے "Piercing Rounds" شامل کیے۔ اس اپ گریڈ سے پہلے، گولی پہلے دشمن پر لگتے ہی رک جاتی تھی۔ اپ گریڈ کے بعد، یہ دشمنوں کے آر پار نکل جاتی ہے، جو ممکنہ طور پر ایک ہی شاٹ میں پوری لائن صاف کر سکتی ہے۔
فرق بہت نمایاں ہے۔ رفتار اور صحت آپ کو زیادہ دیر تک زندہ رہنے میں مدد دے سکتے ہیں، لیکن Piercing Rounds آپ کے پوزیشن لینے کے انداز کو بدل دیتی ہے۔ آپ دشمنوں کو ایک لائن میں لانا شروع کر دیتے ہیں۔ آپ کناروں کے گرد گھومنے (kiting) کے بجائے مرکز سے راستہ بنانا شروع کر دیتے ہیں۔ گیم میں فیصلے کرنے کا دائرہ وسیع ہو جاتا ہے۔
اچھی پروگریشن کو کھلاڑی کے فیصلوں کو بدلنا چاہیے، نہ کہ صرف ان کے نمبروں میں اضافہ کرنا چاہیے۔ اگر ہر اپ گریڈ صرف ایک فیصد اضافہ ہے، تو کھلاڑی تفصیلات پڑھنا چھوڑ دیتے ہیں۔ وہ کلک کرتے ہیں، اپ گریڈ کرتے ہیں، اور بھول جاتے ہیں۔ اگر کوئی اپ گریڈ انہیں اپنی حکمت عملی پر دوبارہ غور کرنے پر مجبور کرتا ہے، تو وہ اسے یاد رکھتے ہیں۔ وہ اس کے بارے میں بات کرتے ہیں۔ وہ یہ دیکھنے کے لیے واپس آتے ہیں کہ مزید کیا چیز گیم کا نقشہ بدل سکتی ہے۔
اصل فائدہ
"ایک اور رن" (one more run) کا لوپ کوئی ایک سسٹم نہیں ہے۔ یہ نقصان اور فائدے کے درمیان ایک تعلق ہے، جو صاف ستھرے آرکیٹیکچر اور بامعنی انعامات پر مبنی ہے۔
دو الگ ٹائم لائنز بنائیں اور Meta Clock کی حفاظت ایسے کریں جیسے یہ آپ کے کھلاڑیوں کا اعتماد ہو، کیونکہ یہ حقیقت میں ایسا ہی ہے۔ لائیو ڈیٹا کو تیز اور پرسسٹنٹ ڈیٹا کو محفوظ رکھنے کے لیے اپنے انجن کے ٹولز کا استعمال کریں۔ اپنے سینز (scenes) کو اسٹوریج سے الگ (decouple) رکھیں تاکہ آپ بغیر کسی خوف کے انہیں بہتر بنا سکیں۔ اور جب آپ اپ گریڈز ڈیزائن کریں، تو خود سے پوچھیں کہ کیا وہ کھلاڑی کو زیادہ وقت دے رہے ہیں، یا زیادہ دلچسپ انتخاب؟
اگر آپ اسے صحیح طریقے سے کر لیں، تو آپ کے کھلاڑی موت کو صرف برداشت نہیں کریں گے، بلکہ وہ اس پر انحصار کریں گے۔ ہر رن اگلے رن کے لیے ایک پیشگی ادائیگی بن جاتا ہے۔ گیم دوبارہ شروع کرنے کا ایک سلسلہ نہیں رہتا بلکہ ایک مسلسل چڑھائی بن جاتا ہے۔
