ہم نے نیٹ ورک لیٹینسی معاوضہ الگورتھم کے ساتھ موبائل شوٹر کے لیے بیلسٹک حسابات کے میکانکس کو کیسے بہتر بنایا

ہم نے نیٹ ورک لیٹینسی معاوضہ الگورتھم کے ساتھ موبائل شوٹر کے لیے بیلسٹک حسابات کے میکانکس کو کیسے بہتر بنایا

ہائے، میں Nikita Brizhak ہوں، Pixonic سے سرور ڈیولپر۔ آج میں موبائل ملٹی پلیئر میں وقفہ کی تلافی کے بارے میں بات کرنا چاہوں گا۔

سرور وقفہ معاوضہ کے بارے میں بہت سے مضامین لکھے گئے ہیں، بشمول روسی زبان میں۔ یہ حیرت کی بات نہیں ہے، کیونکہ یہ ٹیکنالوجی 90 کی دہائی کے آخر سے ملٹی پلیئر FPS کی تخلیق میں فعال طور پر استعمال ہوتی رہی ہے۔ مثال کے طور پر، آپ QuakeWorld موڈ کو یاد کر سکتے ہیں، جو اسے استعمال کرنے والے پہلے لوگوں میں سے ایک تھا۔

ہم اسے اپنے موبائل ملٹی پلیئر شوٹر ڈینو اسکواڈ میں بھی استعمال کرتے ہیں۔

اس آرٹیکل میں، میرا مقصد اس بات کو دہرانا نہیں ہے جو پہلے ہی ہزار بار لکھا جا چکا ہے، بلکہ یہ بتانا ہے کہ ہم نے اپنی ٹیکنالوجی اسٹیک اور گیم پلے کی بنیادی خصوصیات کو مدنظر رکھتے ہوئے، اپنے گیم میں وقفے کے معاوضے کو کیسے نافذ کیا۔

ہمارے کارٹیکس اور ٹیکنالوجی کے بارے میں چند الفاظ۔

ڈینو اسکواڈ ایک نیٹ ورک موبائل PvP شوٹر ہے۔ کھلاڑی مختلف قسم کے ہتھیاروں سے لیس ڈایناسور کو کنٹرول کرتے ہیں اور 6v6 ٹیموں میں ایک دوسرے سے لڑتے ہیں۔

کلائنٹ اور سرور دونوں اتحاد پر مبنی ہیں۔ فن تعمیر شوٹرز کے لیے کافی کلاسک ہے: سرور آمرانہ ہے، اور کلائنٹ کی پیشن گوئی کلائنٹس پر کام کرتی ہے۔ گیم سمولیشن اندرون خانہ ECS کا استعمال کرتے ہوئے لکھا گیا ہے اور سرور اور کلائنٹ دونوں پر استعمال ہوتا ہے۔

اگر یہ آپ نے پہلی بار وقفہ معاوضہ کے بارے میں سنا ہے تو، یہاں اس مسئلے پر ایک مختصر سیر ہے۔

ملٹی پلیئر FPS گیمز میں، میچ عام طور پر ریموٹ سرور پر تیار کیا جاتا ہے۔ کھلاڑی سرور کو اپنا ان پٹ (دبائی گئی چابیاں کے بارے میں معلومات) بھیجتے ہیں، اور جواب میں سرور انہیں موصولہ ڈیٹا کو مدنظر رکھتے ہوئے ایک اپڈیٹ گیم اسٹیٹ بھیجتا ہے۔ اس تعامل کی اسکیم کے ساتھ، فارورڈ کی کو دبانے اور پلیئر کے کردار کے اسکرین پر حرکت کرنے کے درمیان کی تاخیر ہمیشہ پنگ سے زیادہ ہوگی۔

مقامی نیٹ ورکس پر یہ تاخیر (مقبول طور پر ان پٹ وقفہ کہلاتی ہے) ناقابل توجہ ہو سکتی ہے، انٹرنیٹ کے ذریعے کھیلتے وقت یہ کسی کردار کو کنٹرول کرتے وقت "برف پر پھسلنے" کا احساس پیدا کرتی ہے۔ یہ مسئلہ موبائل نیٹ ورکس کے لیے دوگنا متعلقہ ہے، جہاں کسی کھلاڑی کی پنگ 200 ایم ایس کی صورت میں اب بھی ایک بہترین کنکشن سمجھا جاتا ہے۔ اکثر پنگ 350، 500، یا 1000 ایم ایس ہو سکتی ہے۔ پھر ان پٹ وقفہ کے ساتھ تیز شوٹر کھیلنا تقریباً ناممکن ہو جاتا ہے۔

اس مسئلے کا حل کلائنٹ سائڈ سمولیشن پیشن گوئی ہے۔ یہاں کلائنٹ خود سرور سے جواب کا انتظار کیے بغیر پلیئر کریکٹر پر ان پٹ کا اطلاق کرتا ہے۔ اور جب جواب موصول ہوتا ہے، تو یہ محض نتائج کا موازنہ کرتا ہے اور مخالفین کی پوزیشن کو اپ ڈیٹ کرتا ہے۔ اس معاملے میں کلید کو دبانے اور نتیجہ کو اسکرین پر ظاہر کرنے میں تاخیر کم سے کم ہے۔

یہاں نزاکت کو سمجھنا ضروری ہے: کلائنٹ ہمیشہ اپنے آخری ان پٹ کے مطابق خود کو کھینچتا ہے، اور دشمن - نیٹ ورک میں تاخیر کے ساتھ، سرور کے ڈیٹا سے پچھلی حالت کے مطابق۔ یعنی دشمن پر گولی چلاتے وقت کھلاڑی اسے اپنے سے ماضی میں دیکھتا ہے۔ کلائنٹ کی پیشن گوئی کے بارے میں مزید ہم نے پہلے لکھا تھا.

اس طرح، کلائنٹ کی پیشن گوئی ایک مسئلہ حل کرتی ہے، لیکن دوسرا پیدا کرتی ہے: اگر کوئی کھلاڑی اس مقام پر گولی چلاتا ہے جہاں دشمن ماضی میں تھا، سرور پر اسی مقام پر شوٹنگ کرتے وقت، دشمن اب اس جگہ پر نہیں ہوسکتا ہے۔ سرور وقفہ معاوضہ اس مسئلے کو حل کرنے کی کوشش کرتا ہے۔ جب کوئی ہتھیار فائر کیا جاتا ہے، تو سرور گیم اسٹیٹ کو بحال کرتا ہے جسے کھلاڑی نے شاٹ کے وقت مقامی طور پر دیکھا تھا، اور چیک کرتا ہے کہ آیا وہ واقعی دشمن کو مار سکتا تھا۔ اگر جواب "ہاں" ہے، تو ہٹ کو شمار کیا جائے گا، چاہے دشمن اس وقت سرور پر موجود نہ ہو۔

اس علم سے لیس، ہم نے ڈینو اسکواڈ میں سرور وقفہ معاوضہ نافذ کرنا شروع کیا۔ سب سے پہلے، ہمیں یہ سمجھنا تھا کہ کلائنٹ نے جو کچھ دیکھا اسے سرور پر کیسے بحال کیا جائے؟ اور بالکل کیا بحال کرنے کی ضرورت ہے؟ ہمارے کھیل میں، ہتھیاروں اور صلاحیتوں سے حاصل ہونے والی کامیابیوں کا حساب رے کاسٹ اور اوورلیز کے ذریعے کیا جاتا ہے - یعنی دشمن کے جسمانی ٹکرانے والوں کے ساتھ تعامل کے ذریعے۔ اس کے مطابق، ہمیں ان ٹکرانے والوں کی پوزیشن کو دوبارہ پیش کرنے کی ضرورت تھی، جسے کھلاڑی نے سرور پر مقامی طور پر "دیکھا"۔ اس وقت ہم یونٹی ورژن 2018.x استعمال کر رہے تھے۔ وہاں کا فزکس API جامد ہے، طبعی دنیا ایک ہی کاپی میں موجود ہے۔ اس کی ریاست کو بچانے اور پھر اسے باکس سے بحال کرنے کا کوئی طریقہ نہیں ہے۔ تو کیا کرنا ہے؟

حل سطح پر تھا؛ اس کے تمام عناصر ہم نے پہلے ہی دیگر مسائل کو حل کرنے کے لیے استعمال کیے تھے۔

  1. ہر کلائنٹ کے لیے، ہمیں یہ جاننے کی ضرورت ہے کہ اس نے چابیاں دبانے پر مخالفین کو کس وقت دیکھا۔ ہم اس معلومات کو پہلے ہی ان پٹ پیکج میں لکھ چکے ہیں اور اسے کلائنٹ کی پیشن گوئی کو ایڈجسٹ کرنے کے لیے استعمال کیا ہے۔
  2. ہمیں گیم سٹیٹس کی تاریخ کو ذخیرہ کرنے کے قابل ہونے کی ضرورت ہے۔ یہ اسی میں ہے کہ ہم اپنے مخالفین (اور اس وجہ سے ان کے ٹکرانے والے) کے عہدوں پر فائز ہوں گے۔ ہمارے پاس پہلے سے ہی سرور پر ریاستی تاریخ تھی، ہم نے اسے بنانے کے لیے استعمال کیا۔ ڈیلٹا. صحیح وقت کو جاننے سے، ہم تاریخ میں صحیح حالت کو آسانی سے تلاش کر سکتے ہیں۔
  3. اب جب کہ ہمارے پاس تاریخ سے گیم اسٹیٹ ہاتھ میں ہے، ہمیں پلیئر ڈیٹا کو جسمانی دنیا کی حالت کے ساتھ ہم آہنگ کرنے کے قابل ہونے کی ضرورت ہے۔ موجودہ ٹکرانے والے - منتقل کریں، گمشدہ ہیں - تخلیق کریں، غیر ضروری ہیں - تباہ کریں۔ یہ منطق بھی پہلے سے لکھی گئی تھی اور کئی ای سی ایس سسٹمز پر مشتمل تھی۔ ہم نے اسے ایک یونٹی کے عمل میں کئی گیم رومز منعقد کرنے کے لیے استعمال کیا۔ اور چونکہ جسمانی دنیا فی عمل ایک ہے، اس لیے اسے کمروں کے درمیان دوبارہ استعمال کرنا پڑا۔ سمولیشن کے ہر ٹک سے پہلے، ہم نے طبعی دنیا کی حالت کو "ری سیٹ" کیا اور اسے موجودہ کمرے کے ڈیٹا کے ساتھ دوبارہ شروع کیا، ایک چالاک پولنگ سسٹم کے ذریعے یونٹی گیم کی اشیاء کو زیادہ سے زیادہ استعمال کرنے کی کوشش کی۔ جو کچھ باقی رہ گیا وہ ماضی سے گیم سٹیٹ کے لیے اسی منطق کو استعمال کرنا تھا۔

ان تمام عناصر کو ایک ساتھ ملا کر، ہمیں ایک "ٹائم مشین" ملی جو کہ جسمانی دنیا کی حالت کو صحیح وقت پر واپس لے جا سکتی ہے۔ کوڈ سادہ نکلا:

public class TimeMachine : ITimeMachine
{
     //История игровых состояний
     private readonly IGameStateHistory _history;

     //Текущее игровое состояние на сервере
     private readonly ExecutableSystem[] _systems;

     //Набор систем, расставляющих коллайдеры в физическом мире 
     //по данным из игрового состояния
     private readonly GameState _presentState;

     public TimeMachine(IGameStateHistory history, GameState presentState, ExecutableSystem[] timeInitSystems)
     {
         _history = history; 
         _presentState = presentState;
         _systems = timeInitSystems;  
     }

     public GameState TravelToTime(int tick)
     {
         var pastState = tick == _presentState.Time ? _presentState : _history.Get(tick);
         foreach (var system in _systems)
         {
             system.Execute(pastState);
         }
         return pastState;
     }
}

بس صرف یہ معلوم کرنا تھا کہ شاٹس اور صلاحیتوں کی آسانی سے تلافی کرنے کے لیے اس مشین کو کس طرح استعمال کیا جائے۔

سب سے آسان صورت میں، جب میکانکس ایک ہی ہٹ اسکین پر مبنی ہوتے ہیں، تو سب کچھ واضح نظر آتا ہے: کھلاڑی کے گولی مارنے سے پہلے، اسے جسمانی دنیا کو مطلوبہ حالت میں واپس لانے، رے کاسٹ کرنے، ہٹ یا مس کو گننا، اور دنیا کو ابتدائی حالت میں لوٹائیں۔

لیکن ڈنو اسکواڈ میں ایسے میکینکس بہت کم ہیں! گیم میں زیادہ تر ہتھیار پروجیکٹائل بناتے ہیں - طویل عرصے تک چلنے والی گولیاں جو کئی نقلی ٹِکس کے لیے اڑتی ہیں (بعض صورتوں میں، درجنوں ٹِکس)۔ ان کے ساتھ کیا کرنا ہے، وہ کس وقت پرواز کریں؟

В قدیم مضمون ہاف لائف نیٹ ورک اسٹیک کے بارے میں، والو کے لڑکوں نے یہی سوال کیا، اور ان کا جواب یہ تھا: پروجیکٹائل لیگ معاوضہ مشکل ہے، اور اس سے بچنا بہتر ہے۔

ہمارے پاس یہ اختیار نہیں تھا: پروجیکٹائل پر مبنی ہتھیار گیم ڈیزائن کی ایک اہم خصوصیت تھے۔ تو ہمیں کچھ لے کر آنا پڑا۔ کچھ سوچ بچار کے بعد، ہم نے دو آپشنز بنائے جو کام کرتے نظر آئے:

1. ہم پروجیکٹائل کو اس کھلاڑی کے وقت سے باندھتے ہیں جس نے اسے بنایا ہے۔ سرور سمولیشن کی ہر ٹک، ہر کھلاڑی کی ہر گولی کے لیے، ہم فزیکل دنیا کو کلائنٹ کی حالت میں واپس لاتے ہیں اور ضروری حسابات انجام دیتے ہیں۔ اس نقطہ نظر نے سرور پر تقسیم شدہ بوجھ اور پروجیکٹائلوں کی متوقع پرواز کے وقت کو ممکن بنایا۔ پیشن گوئی ہمارے لیے خاص طور پر اہم تھی، کیونکہ ہمارے پاس تمام پروجیکٹائل ہیں، بشمول دشمن کے پراجیکٹائل، کلائنٹ پر پیش گوئی کی گئی ہے۔

ہم نے نیٹ ورک لیٹینسی معاوضہ الگورتھم کے ساتھ موبائل شوٹر کے لیے بیلسٹک حسابات کے میکانکس کو کیسے بہتر بنایا
تصویر میں، ٹک 30 پر کھلاڑی متوقع طور پر ایک میزائل فائر کر رہا ہے: وہ دیکھتا ہے کہ دشمن کس سمت دوڑ رہا ہے اور میزائل کی تخمینی رفتار کو جانتا ہے۔ مقامی طور پر وہ دیکھتا ہے کہ اس نے 33ویں ٹک پر ہدف کو نشانہ بنایا۔ وقفہ معاوضہ کا شکریہ، یہ سرور پر بھی ظاہر ہوگا۔

2. ہم سب کچھ ویسا ہی کرتے ہیں جیسا کہ پہلے آپشن میں کیا گیا تھا، لیکن، بلٹ سمولیشن کی ایک ٹک گننے کے بعد، ہم نہیں رکتے، بلکہ اسی سرور ٹک کے اندر اس کی پرواز کی نقل کرتے رہتے ہیں، ہر بار اپنے وقت کو سرور کے قریب لاتے ہیں۔ ایک ایک کرکے ٹک اور ٹکرانے والی پوزیشنوں کو اپ ڈیٹ کرنا۔ ہم یہ اس وقت تک کرتے ہیں جب تک کہ دو چیزوں میں سے ایک نہ ہوجائے:

  • گولی ختم ہو چکی ہے۔ اس کا مطلب ہے کہ حساب ختم ہو گیا ہے، ہم ایک مس یا ہٹ گن سکتے ہیں۔ اور یہ اسی ٹک پر ہے جس میں گولی چلائی گئی تھی! ہمارے لیے یہ پلس اور مائنس دونوں تھا۔ ایک پلس - کیونکہ شوٹنگ کے کھلاڑی کے لئے اس نے ہٹ اور دشمن کی صحت میں کمی کے درمیان تاخیر کو نمایاں طور پر کم کردیا۔ منفی پہلو یہ ہے کہ ایک ہی اثر اس وقت دیکھا گیا جب مخالفین نے کھلاڑی پر گولی چلائی: ایسا لگتا ہے کہ دشمن نے صرف ایک سست راکٹ فائر کیا تھا، اور نقصان پہلے ہی شمار ہو چکا تھا۔
  • گولی سرور کے وقت پر پہنچ گئی ہے۔ اس صورت میں، اس کا سمولیشن اگلے سرور ٹک میں بغیر کسی وقفے کے معاوضے کے جاری رہے گا۔ سست پروجیکٹائل کے لیے، یہ نظریاتی طور پر پہلے آپشن کے مقابلے میں فزکس رول بیکس کی تعداد کو کم کر سکتا ہے۔ ایک ہی وقت میں، تخروپن پر غیر مساوی بوجھ بڑھ گیا: سرور یا تو بیکار تھا، یا ایک سرور ٹک میں یہ کئی گولیوں کے لیے درجن بھر نقلی ٹک کا حساب لگا رہا تھا۔

ہم نے نیٹ ورک لیٹینسی معاوضہ الگورتھم کے ساتھ موبائل شوٹر کے لیے بیلسٹک حسابات کے میکانکس کو کیسے بہتر بنایا
پچھلی تصویر کے طور پر ایک ہی منظر، لیکن دوسری سکیم کے مطابق حساب. میزائیل سرور کے وقت کے ساتھ اسی ٹک پر "پکڑا گیا" جس پر گولی لگی تھی، اور ہٹ کو اگلی ٹک کے طور پر شمار کیا جا سکتا ہے۔ 31 ویں ٹک پر، اس معاملے میں، وقفہ معاوضہ مزید لاگو نہیں ہوتا ہے۔

ہمارے نفاذ میں، یہ دونوں نقطہ نظر کوڈ کی صرف دو لائنوں میں مختلف تھے، لہذا ہم نے دونوں کو تخلیق کیا، اور ایک طویل عرصے تک وہ متوازی طور پر موجود تھے۔ ہتھیار کے میکانکس اور گولی کی رفتار پر منحصر ہے، ہم نے ہر ڈایناسور کے لیے ایک یا دوسرے آپشن کا انتخاب کیا۔ یہاں اہم موڑ میکینکس کے کھیل میں ظاہر ہوا جیسے "اگر آپ فلاں فلاں وقت میں دشمن کو اتنی بار مارتے ہیں تو فلاں فلاں بونس حاصل کریں۔" کوئی بھی مکینک جہاں کھلاڑی نے جس وقت دشمن کو نشانہ بنایا اس نے اہم کردار ادا کیا دوسرے نقطہ نظر کے ساتھ کام کرنے سے انکار کر دیا۔ لہذا ہم نے پہلے آپشن کے ساتھ جانا ختم کیا، اور اب یہ گیم میں موجود تمام ہتھیاروں اور تمام فعال صلاحیتوں پر لاگو ہوتا ہے۔

الگ سے، یہ کارکردگی کا مسئلہ اٹھانے کے قابل ہے۔ اگر آپ سوچتے ہیں کہ یہ سب چیزوں کو سست کردے گا، تو میں جواب دیتا ہوں: ایسا ہے۔ اتحاد ٹکرانے والوں کو حرکت دینے اور انہیں آن اور آف کرنے میں کافی سست ہے۔ ڈینو اسکواڈ میں، "بدترین" صورت میں، لڑائی میں بیک وقت کئی سو پروجیکٹائل موجود ہو سکتے ہیں۔ ہر پروجیکٹائل کو انفرادی طور پر شمار کرنے کے لیے ٹکراؤ کو منتقل کرنا ایک ناقابل برداشت عیش و آرام ہے۔ لہٰذا، ہمارے لیے یہ بالکل ضروری تھا کہ طبیعیات کی "رول بیکس" کی تعداد کو کم سے کم کیا جائے۔ ایسا کرنے کے لیے، ہم نے ECS میں ایک الگ جزو بنایا جس میں ہم کھلاڑی کا وقت ریکارڈ کرتے ہیں۔ ہم نے اسے ان تمام اداروں میں شامل کیا جن کے لیے وقفہ معاوضہ کی ضرورت ہوتی ہے (پروجیکٹائلز، صلاحیتیں وغیرہ)۔ اس سے پہلے کہ ہم ایسی ہستیوں پر کارروائی شروع کریں، ہم انہیں اس وقت تک کلسٹر کرتے ہیں اور ان کو ایک ساتھ پروسیس کرتے ہیں، ہر ایک کلسٹر کے لیے ایک بار فزیکل دنیا کو رول بیک کرتے ہیں۔

اس مرحلے پر ہمارے پاس عام طور پر کام کرنے والا نظام ہے۔ اس کا کوڈ کسی حد تک آسان شکل میں:

public sealed class LagCompensationSystemGroup : ExecutableSystem
{
     //Машина времени
     private readonly ITimeMachine _timeMachine;

     //Набор систем лагкомпенсации
     private readonly LagCompensationSystem[] _systems;
     
     //Наша реализация кластеризатора
     private readonly TimeTravelMap _travelMap = new TimeTravelMap();

    public LagCompensationSystemGroup(ITimeMachine timeMachine, 
        LagCompensationSystem[] lagCompensationSystems)
     {
         _timeMachine = timeMachine;
         _systems = lagCompensationSystems;
     }

     public override void Execute(GameState gs)
     {
         //На вход кластеризатор принимает текущее игровое состояние,
         //а на выход выдает набор «корзин». В каждой корзине лежат энтити,
         //которым для лагкомпенсации нужно одно и то же время из истории.
         var buckets = _travelMap.RefillBuckets(gs);

         for (int bucketIndex = 0; bucketIndex < buckets.Count; bucketIndex++)
         {
             ProcessBucket(gs, buckets[bucketIndex]);
         }

         //В конце лагкомпенсации мы восстанавливаем физический мир 
         //в исходное состояние
         _timeMachine.TravelToTime(gs.Time);
     }

     private void ProcessBucket(GameState presentState, TimeTravelMap.Bucket bucket)
     {
         //Откатываем время один раз для каждой корзины
         var pastState = _timeMachine.TravelToTime(bucket.Time);

         foreach (var system in _systems)
         {
               system.PastState = pastState;
               system.PresentState = presentState;

               foreach (var entity in bucket)
               {
                   system.Execute(entity);
               }
          }
     }
}

جو کچھ باقی تھا وہ تفصیلات کو ترتیب دینا تھا:

1. سمجھیں کہ وقت میں نقل و حرکت کے زیادہ سے زیادہ فاصلے کو کتنا محدود کرنا ہے۔

کمزور موبائل نیٹ ورکس کے حالات میں گیم کو ہر ممکن حد تک قابل رسائی بنانا ہمارے لیے ضروری تھا، اس لیے ہم نے کہانی کو 30 ٹک کے مارجن (20 ہرٹز کی ٹک ریٹ کے ساتھ) تک محدود کر دیا۔ یہ کھلاڑیوں کو بہت زیادہ پنگوں پر بھی مخالفین کو مارنے کی اجازت دیتا ہے۔

2. اس بات کا تعین کریں کہ کون سی اشیاء کو وقت کے ساتھ منتقل کیا جا سکتا ہے اور کون سے نہیں۔

ہم یقیناً اپنے مخالفین کو آگے بڑھا رہے ہیں۔ لیکن قابل تنصیب توانائی کی شیلڈز، مثال کے طور پر، نہیں ہیں۔ ہم نے فیصلہ کیا کہ دفاعی صلاحیت کو ترجیح دینا بہتر ہے، جیسا کہ اکثر آن لائن شوٹرز میں کیا جاتا ہے۔ اگر کھلاڑی نے پہلے ہی حال میں ڈھال رکھ دی ہے، تو ماضی کی وقفے وقفے سے معاوضہ لینے والی گولیوں کو اس کے ذریعے نہیں اڑنا چاہیے۔

3. فیصلہ کریں کہ کیا ڈائنوسار کی صلاحیتوں کی تلافی کرنا ضروری ہے: کاٹنا، دم سے مارنا، وغیرہ۔ ہم نے فیصلہ کیا کہ کیا ضرورت ہے اور گولیوں کی طرح انہی اصولوں کے مطابق کارروائی کی۔

4. تعین کریں کہ اس کھلاڑی کے ٹکرانے والوں کے ساتھ کیا کرنا ہے جس کے لیے وقفہ معاوضہ ادا کیا جا رہا ہے۔ اچھے طریقے سے، ان کی پوزیشن ماضی میں نہیں بدلنی چاہیے: کھلاڑی کو خود کو اسی وقت دیکھنا چاہیے جس میں وہ اب سرور پر ہے۔ تاہم، ہم شوٹنگ پلیئر کے ٹکرانے والے کو بھی واپس کر دیتے ہیں، اور اس کی کئی وجوہات ہیں۔

سب سے پہلے، یہ کلسٹرنگ کو بہتر بناتا ہے: ہم قریبی پنگ والے تمام کھلاڑیوں کے لیے ایک جیسی جسمانی حالت استعمال کر سکتے ہیں۔

دوم، تمام رے کاسٹ اور اوورلیپ میں ہم ہمیشہ اس کھلاڑی کے ٹکرانے والے کو خارج کرتے ہیں جو صلاحیتوں یا پروجیکٹائلز کا مالک ہے۔ ڈینو اسکواڈ میں، کھلاڑی ڈائنوسار کو کنٹرول کرتے ہیں، جن میں شوٹر کے معیار کے مطابق جیومیٹری غیر معیاری ہوتی ہے۔ یہاں تک کہ اگر کھلاڑی ایک غیر معمولی زاویے سے گولی چلاتا ہے اور گولی کی رفتار کھلاڑی کے ڈائنوسار ٹکرانے والے سے گزرتی ہے، گولی اسے نظر انداز کر دے گی۔

تیسرا، ہم وقفہ معاوضہ شروع ہونے سے پہلے ہی ڈایناسور کے ہتھیار کی پوزیشن یا ECS سے ڈیٹا استعمال کرنے کی صلاحیت کے نقطہ نظر کا حساب لگاتے ہیں۔

نتیجے کے طور پر، وقفے سے معاوضہ لینے والے کھلاڑی کے ٹکرانے والوں کی اصل پوزیشن ہمارے لیے غیر اہم ہے، اس لیے ہم نے زیادہ نتیجہ خیز اور ساتھ ہی آسان راستہ اختیار کیا۔

نیٹ ورک لیٹینسی کو آسانی سے نہیں ہٹایا جا سکتا، اسے صرف ماسک کیا جا سکتا ہے۔ بھیس ​​بدلنے کے کسی دوسرے طریقے کی طرح، سرور کے وقفے کے معاوضے میں بھی اس کی تجارت ہوتی ہے۔ یہ اس کھلاڑی کے گیمنگ کے تجربے کو بہتر بناتا ہے جو گولی مارنے والے کھلاڑی کی قیمت پر شوٹنگ کر رہا ہے۔ ڈینو اسکواڈ کے لیے، تاہم، یہاں انتخاب واضح تھا۔

بلاشبہ، یہ سب کچھ مجموعی طور پر سرور کوڈ کی بڑھتی ہوئی پیچیدگی سے بھی ادا کرنا پڑا - پروگرامرز اور گیم ڈیزائنرز دونوں کے لیے۔ اگر پہلے تخروپن نظاموں کی ایک سادہ ترتیب وار کال تھی، تو وقفے کے معاوضے کے ساتھ، اس میں نیسٹڈ لوپس اور شاخیں نمودار ہوئیں۔ ہم نے اس کے ساتھ کام کرنے میں آسانی پیدا کرنے کے لیے کافی کوششیں بھی کیں۔

2019 ورژن میں (اور شاید تھوڑا پہلے)، یونٹی نے آزاد جسمانی مناظر کے لیے مکمل تعاون شامل کیا۔ ہم نے انہیں اپ ڈیٹ کے تقریباً فوراً بعد سرور پر لاگو کر دیا، کیونکہ ہم تمام کمروں میں مشترکہ جسمانی دنیا سے فوری طور پر چھٹکارا حاصل کرنا چاہتے تھے۔

ہم نے ہر گیم روم کو اس کا اپنا جسمانی منظر دیا اور اس طرح نقلی حساب لگانے سے پہلے پڑوسی کمرے کے ڈیٹا سے منظر کو "صاف" کرنے کی ضرورت کو ختم کردیا۔ سب سے پہلے، اس نے پیداواری صلاحیت میں نمایاں اضافہ کیا۔ دوم، اس نے کیڑے کی ایک پوری کلاس سے چھٹکارا حاصل کرنا ممکن بنایا جو پیدا ہوتا ہے اگر پروگرامر نے نئے گیم عناصر کو شامل کرتے وقت سین کلین اپ کوڈ میں غلطی کی ہے۔ اس طرح کی غلطیوں کو ڈیبگ کرنا مشکل تھا، اور ان کے نتیجے میں اکثر ایک کمرے کے منظر میں جسمانی اشیاء کی حالت دوسرے کمرے میں "بہہ" جاتی تھی۔

اس کے علاوہ، ہم نے کچھ تحقیق کی کہ آیا جسمانی مناظر کو طبعی دنیا کی تاریخ کو ذخیرہ کرنے کے لیے استعمال کیا جا سکتا ہے۔ یعنی، مشروط طور پر، ہر کمرے کے لیے ایک سین نہیں، بلکہ 30 سین مختص کریں، اور ان میں سے ایک سائیکلک بفر بنائیں، جس میں کہانی کو محفوظ کیا جائے۔ عام طور پر، آپشن کام کر رہا تھا، لیکن ہم نے اسے نافذ نہیں کیا: اس نے پیداوری میں کوئی پاگل اضافہ نہیں دکھایا، بلکہ خطرناک تبدیلیوں کی ضرورت تھی۔ یہ اندازہ لگانا مشکل تھا کہ اتنے سارے مناظر کے ساتھ طویل عرصے تک کام کرنے پر سرور کیسا برتاؤ کرے گا۔ لہذا، ہم نے اصول کی پیروی کی: "اگر یہ ٹوٹ نہیں ہے تو اسے ٹھیک نہ کریں'.

ماخذ: www.habr.com

نیا تبصرہ شامل کریں