چگونه میتوان یک بازی شهرسازی را روی پردازنده ۱۶ مگاهرتزی اجرا کرد؟ — How to run a city builder on 16 MHz
خلاصه
این مقاله بررسی میکند که چگونه بازی کلاسیک SimCity با پردازندههای ضعیف گذشته سازگار شده است. با نگاهی به سورسکد Micropolis، تکنیک توزیع بار پردازشی در فریمهای مختلف برای بهینهسازی کدهای گیمپلی توضیح داده میشود.
مقدمه
امروزه سختافزارهای مدرن با پردازندههای چند هستهای و فرکانسهای گیگاهرتزی به امری عادی تبدیل شدهاند؛ به طوری که اجرای نرمافزارها روی سختافزارهای ضعیف گذشته مانند داستانهای تخیلی به نظر میرسد. با این حال، در دوران پردازندههای ۱۶ مگاهرتزی، توسعهدهندگان بازیهای بزرگی مثل SimCity را روی سیستمهایی با مشخصات بسیار محدود اجرا میکردند. این مقاله با بررسی سورسکد اصلی بازی، به این پرسش پاسخ میدهد که راز این بهینهسازی چه بوده است.
واکاوی کدهای SimCity از طریق Micropolis
در سال ۲۰۰۸ بود که سورسکد نسخه اصلی SimCity تحت مجوز GPL و با نام Micropolis منتشر شد. این مخزن منبعی ارزشمند برای مهندسان نرمافزار است تا نحوه مدیریت پردازشهای سنگین در سیستمهای قدیمی را بررسی کنند. بخش عمدهای از بهینهسازیها در کدهای گرافیکی نیست، بلکه در منطق شبیهسازی شهر (Simulation Core) نهفته است.
هسته اصلی شبیهسازی در تابعی به نام Simulate() قرار دارد که وظیفه بهروزرسانی سیستمهای مختلف نظیر ترافیک، آتشسوزی، جرم و جنایت و وصول مالیات را بر عهده دارد. فراخوانی همزمان تمام این زیرسیستمها در هر فریم، پردازندههای ضعیف آن دوران را از پای درمیآورد.
ترفند اصلی: پرهیز از اجرای همه چیز در هر فریم
راهحل توسعهدهندگان بسیار ساده اما هوشمندانه بود: اجرا نکردن تمام توابع در هر فریم.
به جای استفاده از متغیر سنتی deltaTime برای محاسبه زمان فریم، تابع شبیهسازی از یک پارامتر حلقوی به نام mod16 (با مقادیر بین صفر تا ۱۵) استفاده میکند. این پارامتر تعیین میکند که در فریمِ جاری، کدام بخش از شبیهسازی باید اجرا شود.
مزایا و ویژگیهای این روش عبارتند از:
- کاهش چشمگیر بار پردازشی در هر فریم.
- تأخیر واکنش سیستم به اقدامات بازیکن بهطور میانگین حدود ۸ فریم و در بدترین حالت ۱۵ فریم.
- عدم تاثیر منفی روی تجربه کاربری؛ زیرا در یک بازی شهرسازی، تأخیر نامحسوس به چشم نمیآید و گیمپلی روان میماند.
الگوبرداری در بازیهای مدرن
این ایده تقسیم وظایف بین فریمهای مختلف محدود به بازیهای رترو نیست. برای نمونه، در بازی مدرن Assetto Corsa برای رندر بازتابها (Reflections) از Cubemapها استفاده میشود. به جای بهروزرسانی تمام وجههای Cubemap در هر فریم، بازی با استفاده از متغیر FACES_PER_FRAME تنها تعداد محدودی از وجوه را در هر فریم بهروزرسانی میکند تا نرخ فریم (FPS) حفظ شود.
همچنین موتورهای بازیسازی مدرن مانند Unreal Engine با تکنیکهایی نظیر Lumen، نورپردازی را در تمام فریمها بهطور کامل از نو محاسبه نمیکنند که رویکردی مشابه همین الگوی بهینهسازی است.
نگاه جامعه هکرنیوز
در بحثهای پیرامون این مقاله در Hacker News، کاربران به نکات جالبی اشاره کردند:
- برخی یادآوری کردند که نسخههای اولیه SimCity حتی روی سختافزارهای ضعیفتر مانند سیستمهای مجهز به تراشههای ۴.۷۷ مگاهرتزی یا پردازندههای ۸ بیتی Commodore 64 و Game Boy Color (با معماریهای ۶۵۰۲ و Z80) نیز پورت و اجرا شدهاند که نشاندهنده شاهکارهای مهندسی نرمافزار در محدودیت سختافزاری است.
چرا این موضوع برای مهندسان نرمافزار اهمیت دارد؟
هنگام توسعه کدهای گیمپلی یا سیستمهای پردازشی سنگین، این ایده به ما یادآوری میکند که نباید کورکورانه تمام منطقها را در هر چرخهی پردازشی (Tick/Frame) اجرا کنیم. پرسش کلیدی این است: «آیا واقعاً لازم است این بخش از کد در هر فریم اجرا شود؟»
این مطلب بهصورت کاملاً خودکار و با هوش مصنوعی از مقالهٔ اصلی و بحث هکرنیوز تهیه شده و ممکن است خطا داشته باشد. حقوق مطلب اصلی متعلق به نویسندهٔ آن است. قوانین و گزارش مشکل