تجربه هابیت

با موبایل WebGL، سرزمین میانه را زنده کنید

از لحاظ تاریخی، آوردن تجربه‌های تعاملی، مبتنی بر وب و چندرسانه‌ای سنگین به تلفن‌های همراه و تبلت‌ها یک چالش بوده است. محدودیت‌های اصلی عملکرد، در دسترس بودن API، محدودیت‌های صوتی HTML5 در دستگاه‌ها و عدم پخش یکپارچه ویدیوی درون خطی بوده است.

اوایل امسال، پروژه‌ای را با دوستانی از Google و Warner Bros آغاز کردیم تا یک تجربه وب برای اولین بار در موبایل برای فیلم جدید هابیت، The Hobbit: The Desolation of Smaug ایجاد کنیم. ساختن یک آزمایش چندرسانه‌ای Chrome برای موبایل، کاری واقعاً الهام‌بخش و چالش‌برانگیز بوده است.

این تجربه برای Chrome برای Android در دستگاه‌های جدید Nexus که اکنون به WebGL و Web Audio دسترسی داریم، بهینه شده است. با این حال، بخش بزرگی از تجربه در دستگاه‌ها و مرورگرهای غیر WebGL و همچنین به لطف ترکیب‌بندی سریع سخت‌افزاری و انیمیشن‌های CSS قابل دسترسی است.

کل تجربه بر اساس نقشه ای از سرزمین میانه و مکان ها و شخصیت های فیلم های هابیت است. استفاده از WebGL این امکان را برای ما فراهم کرد تا دنیای غنی سه گانه هابیت را به نمایش بگذاریم و کشف کنیم و به کاربران اجازه دهیم این تجربه را کنترل کنند.

چالش های WebGL در دستگاه های تلفن همراه

اولاً، اصطلاح "دستگاه های تلفن همراه" بسیار گسترده است. مشخصات دستگاه ها بسیار متفاوت است. بنابراین به‌عنوان یک توسعه‌دهنده باید تصمیم بگیرید که آیا می‌خواهید از دستگاه‌های بیشتری با تجربه پیچیده‌تر پشتیبانی کنید یا، همانطور که در این مورد انجام دادیم، دستگاه‌های پشتیبانی‌شده را محدود به دستگاه‌هایی کنید که می‌توانند دنیای سه‌بعدی واقعی‌تری را به نمایش بگذارند. برای «سفر از طریق سرزمین میانه» ما بر روی دستگاه‌های Nexus و پنج گوشی هوشمند اندرویدی محبوب تمرکز کردیم.

در آزمایش، همانطور که برای برخی از پروژه های WebGL قبلی خود انجام داده بودیم، از three.js استفاده کردیم. ما پیاده سازی را با ساختن نسخه اولیه بازی Trollshaw آغاز کردیم که به خوبی روی تبلت Nexus 10 اجرا می شد. پس از چند آزمایش اولیه روی دستگاه، لیستی از بهینه‌سازی‌ها را در ذهن داشتیم که بسیار شبیه به آنچه که معمولاً برای یک لپ‌تاپ با مشخصات پایین استفاده می‌کردیم، به نظر می‌رسید:

  • از مدل های کم پلی استفاده کنید
  • از بافت های کم رزولوشن استفاده کنید
  • با ادغام هندسه تا حد امکان تعداد فراخوان ها را کاهش دهید
  • مواد و نور را ساده کنید
  • افکت های پست را حذف کنید و آنتی آلیاسینگ را خاموش کنید
  • بهینه سازی عملکرد جاوا اسکریپت
  • بوم WebGL را در اندازه نصف کنید و با CSS بزرگ کنید

پس از اعمال این بهینه‌سازی‌ها در اولین نسخه خشن خود از بازی، نرخ فریم ثابت 30 فریم در ثانیه داشتیم که از آن راضی بودیم. در آن مرحله هدف ما بهبود تصاویر بدون تأثیر منفی بر نرخ فریم بود. ما ترفندهای زیادی را امتحان کردیم: برخی از آنها واقعاً بر عملکرد تأثیر داشتند. تعداد کمی آنقدر که ما امیدوار بودیم تأثیری نداشتند.

از مدل های کم پلی استفاده کنید

بیایید با مدل ها شروع کنیم. استفاده از مدل های کم پلی مطمئناً به زمان دانلود و همچنین زمان لازم برای مقداردهی اولیه صحنه کمک می کند. ما متوجه شدیم که می‌توانیم پیچیدگی را تا حد زیادی افزایش دهیم بدون اینکه تأثیر زیادی روی عملکرد داشته باشیم. مدل های ترول که ما در این بازی استفاده می کنیم حدود 5K چهره هستند و صحنه حدودا 40K چهره است و به خوبی کار می کند.

یکی از ترول های جنگل ترولشاو
یکی از ترول های جنگل ترولشاو

برای مکان دیگری (هنوز منتشر نشده) در تجربه، شاهد تأثیر بیشتری بر عملکرد کاهش چند ضلعی ها بودیم. در آن صورت، ما اشیاء چند ضلعی پایین‌تر را برای دستگاه‌های تلفن همراه نسبت به اشیایی که برای دسک‌تاپ بارگذاری کرده‌ایم، بارگذاری کردیم. ایجاد مجموعه‌های مختلف مدل‌های سه‌بعدی نیاز به کار اضافی دارد و همیشه لازم نیست. این واقعاً به پیچیدگی مدل های شما برای شروع بستگی دارد.

وقتی روی صحنه‌های بزرگ با اشیاء زیاد کار می‌کردیم، سعی کردیم در نحوه تقسیم هندسه استراتژیک باشیم. این به ما امکان داد تا شبکه های کمتر مهم را به سرعت روشن و خاموش کنیم تا تنظیماتی را پیدا کنیم که برای همه دستگاه های تلفن همراه کار می کند. سپس، می‌توانیم هندسه را در جاوا اسکریپت در زمان اجرا برای بهینه‌سازی پویا ادغام کنیم یا آن را در مرحله پیش تولید برای ذخیره درخواست‌ها ادغام کنیم.

از بافت های کم رزولوشن استفاده کنید

برای کاهش زمان بارگذاری در دستگاه‌های تلفن همراه، ما انتخاب کردیم که بافت‌های مختلفی را که نصف اندازه بافت‌ها روی دسک‌تاپ بود بارگیری کنیم. به نظر می رسد که همه دستگاه ها می توانند اندازه بافت تا 2048x2048 پیکسل را مدیریت کنند و اکثر آنها می توانند 4096x4096 پیکسل را مدیریت کنند. به نظر نمی رسد که جستجوی بافت روی تکستچرها پس از آپلود در GPU مشکلی ایجاد نکند. اندازه کل بافت ها باید در حافظه GPU قرار گیرد تا از بالا رفتن و دانلود مداوم بافت ها جلوگیری شود، اما این احتمالاً برای اکثر تجربیات وب مشکل بزرگی نیست. با این حال، ترکیب بافت‌ها در کمترین حد ممکن برای کاهش تعداد تماس‌ها مهم است - این چیزی است که تأثیر زیادی بر عملکرد دستگاه‌های تلفن همراه دارد.

بافت برای یکی از ترول های جنگل ترولشاو
بافت برای یکی از ترول های جنگل ترولشاو
(اندازه اصلی 512x512 پیکسل)

مواد و نور را ساده کنید

انتخاب مواد نیز می‌تواند بر عملکرد تأثیر بسزایی داشته باشد و باید در تلفن همراه هوشمندانه مدیریت شود. استفاده از MeshLambertMaterial (محاسبه در هر راس نور) در three.js به جای MeshPhongMaterial (محاسبه در هر تکسل نور) یکی از مواردی است که ما برای بهینه سازی عملکرد استفاده کردیم. اساساً ما سعی کردیم از سایه بان های ساده با کمترین محاسبات نور ممکن استفاده کنیم.

برای اینکه ببینید موادی که استفاده می‌کنید چگونه بر عملکرد یک صحنه تأثیر می‌گذارند، می‌توانید مواد صحنه را با یک MeshBasicMaterial لغو کنید. این به شما مقایسه خوبی می دهد.

scene.overrideMaterial = new THREE.MeshBasicMaterial({color:0x333333, wireframe:true});

بهینه سازی عملکرد جاوا اسکریپت

هنگام ساخت بازی برای موبایل، GPU همیشه بزرگترین مانع نیست. زمان زیادی برای CPU صرف می شود، به خصوص فیزیک و انیمیشن های اسکلتی. یکی از ترفندهایی که گاهی اوقات کمک می کند، بسته به شبیه سازی، این است که فقط این محاسبات گران قیمت را در هر فریم دیگر اجرا کنید. همچنین می‌توانید از تکنیک‌های بهینه‌سازی جاوا اسکریپت در مورد جمع‌آوری اشیاء، جمع‌آوری زباله و ایجاد شی استفاده کنید.

به روز رسانی اشیاء از پیش تخصیص داده شده در حلقه ها به جای ایجاد اشیاء جدید گام مهمی برای جلوگیری از "سکسکه" جمع آوری زباله در طول بازی است.

به عنوان مثال، کدی مانند این را در نظر بگیرید:

var currentPos = new THREE.Vector3();

function gameLoop() {
  currentPos = new THREE.Vector3(0+offsetX,100,0);
}

یک نسخه بهبود یافته از این حلقه از ایجاد اشیاء جدید که باید زباله جمع آوری شوند جلوگیری می کند:

var originPos = new THREE.Vector3(0,100,0);
var currentPos = new THREE.Vector3();
function gameLoop() {
  currentPos.copy(originPos).x += offsetX;
  //or
  currentPos.set(originPos.x+offsetX,originPos.y,originPos.z);
}

تا جایی که امکان دارد، کنترل‌کننده‌های رویداد فقط باید ویژگی‌ها را به‌روزرسانی کنند و به‌روزرسانی حلقه رندر requestAnimationFrame اجازه دهند تا مرحله را به‌روزرسانی کند.

نکته دیگر بهینه سازی و/یا پیش محاسبه عملیات ریخته گری پرتو است. به عنوان مثال، اگر در طول یک حرکت مسیر ایستا نیاز دارید که یک شی را به مش متصل کنید، می توانید موقعیت ها را در طول یک حلقه "ثبت کنید" و سپس به جای ریختن پرتو در مقابل مش، از این داده ها بخوانید. یا همانطور که در تجربه ریوندل انجام می‌دهیم، برای جستجوی تعاملات ماوس با یک شبکه نامرئی ساده‌تر با پلی پایین، از پرتو کاست. جستجوی برخورد روی یک توری پلی بالا بسیار آهسته است و به طور کلی باید از آن در یک حلقه بازی اجتناب کرد.

بوم WebGL را در اندازه نصف کنید و با CSS بزرگ کنید

اندازه بوم WebGL احتمالاً تنها مؤثرترین پارامتری است که می توانید برای بهینه سازی عملکرد آن را تغییر دهید. هرچه بوم بزرگتری برای ترسیم صحنه سه بعدی خود استفاده کنید، پیکسل های بیشتری باید روی هر فریم کشیده شود. این البته بر عملکرد تأثیر می‌گذارد. Nexus 10 با نمایشگر 2560x1600 پیکسلی با چگالی بالا، باید 4 برابر بیشتر از یک تبلت با تراکم پایین، تعداد پیکسل‌ها را فشار دهد. برای بهینه‌سازی این مورد برای موبایل، از ترفندی استفاده می‌کنیم که در آن بوم را به نصف اندازه (50%) تنظیم می‌کنیم و سپس با تبدیل‌های CSS 3D با شتاب سخت‌افزاری، آن را تا اندازه مورد نظر خود (100%) افزایش می‌دهیم. نقطه ضعف این یک تصویر پیکسلی است که در آن خطوط نازک می توانند مشکل ساز شوند، اما در صفحه نمایش با وضوح بالا این اثر آنقدرها بد نیست. این کاملا ارزش عملکرد اضافی را دارد.

همان صحنه بدون پوسته‌بندی بوم در Nexus 10 (16FPS) و مقیاس‌بندی شده تا 50% (33FPS)
همان صحنه بدون پوسته‌بندی بوم در Nexus 10 (16 فریم در ثانیه) و مقیاس‌بندی تا 50 درصد (33 فریم در ثانیه).

اشیاء به عنوان بلوک های سازنده

برای اینکه بتوانیم پیچ و خم بزرگ قلعه Dol Guldur و دره بی پایان ریوندل را ایجاد کنیم، مجموعه ای از مدل های سه بعدی بلوک ساختمانی را ساختیم که دوباره از آنها استفاده می کنیم. استفاده مجدد از اشیاء به ما امکان می دهد اطمینان حاصل کنیم که اشیاء در ابتدای تجربه و نه در وسط آن نمونه سازی و آپلود می شوند.

بلوک های ساختمانی شی سه بعدی مورد استفاده در پیچ و خم دول گولدور.
بلوک های ساختمانی شی سه بعدی مورد استفاده در پیچ و خم دول گولدور.

در ریوندل ما تعدادی بخش زمینی داریم که با پیشرفت سفر کاربر، دائماً در عمق Z تغییر مکان می دهیم. همانطور که کاربر بخش ها را عبور می دهد، این بخش ها در فاصله دور تغییر مکان می دهند.

برای قلعه Dol Guldur ما می خواستیم پیچ و خم برای هر بازی بازسازی شود. برای انجام این کار ما یک اسکریپت ایجاد کردیم که پیچ و خم را بازسازی می کند.

ادغام کل ساختار در یک شبکه بزرگ از ابتدا منجر به یک صحنه بسیار بزرگ و عملکرد ضعیف می شود. برای رسیدگی به این موضوع، تصمیم گرفتیم بلوک‌های ساختمان را بسته به اینکه در معرض دید هستند پنهان و نشان دهیم. از همان ابتدا، ما ایده ای در مورد استفاده از یک اسکریپت raycaster دو بعدی داشتیم، اما در پایان از حذف داخلی three.js frustrum استفاده کردیم. ما از اسکریپت raycaster برای بزرگنمایی روی «خطری» که بازیکن با آن مواجه است، دوباره استفاده کردیم.

نکته مهم بعدی تعامل با کاربر است. در دسکتاپ ورودی ماوس و صفحه کلید دارید. در دستگاه های تلفن همراه کاربران شما با لمس، کشیدن انگشت، نیشگون گرفتن، جهت گیری دستگاه و غیره تعامل دارند.

استفاده از تعامل لمسی در تجربیات وب تلفن همراه

افزودن پشتیبانی لمسی کار سختی نیست. مقالات بسیار خوبی برای خواندن در مورد موضوع وجود دارد. اما موارد کوچکی وجود دارد که می تواند آن را پیچیده تر کند.

می توانید هم لمسی و هم ماوس داشته باشید. Chromebook Pixel و سایر لپ‌تاپ‌های لمسی دارای پشتیبانی از ماوس و لمس هستند. یکی از اشتباهات رایج این است که بررسی کنید آیا دستگاه لمسی فعال است یا خیر و سپس فقط شنوندگان رویدادهای لمسی را اضافه کنید و هیچ کدام برای ماوس وجود ندارد.

رندر را در شنوندگان رویداد به‌روزرسانی نکنید. به جای آن رویدادهای لمسی را در متغیرها ذخیره کنید و در حلقه رندر requestAnimationFrame به آنها واکنش نشان دهید. این عملکرد را بهبود می بخشد و همچنین رویدادهای متضاد را با هم ترکیب می کند. اطمینان حاصل کنید که به جای ایجاد اشیاء جدید در شنوندگان رویداد، از اشیاء دوباره استفاده می کنید.

به یاد داشته باشید که چند لمسی است: event.touches آرایه ای از تمام لمس ها است. در برخی موارد جالب‌تر است که به جای آن به event.targetTouches یا event.changedTouches نگاه کنید و فقط به لمس‌هایی که به آن‌ها علاقه دارید واکنش نشان دهید. برای جدا کردن ضربه‌ها از ضربه‌ها، قبل از اینکه بررسی کنیم لمس حرکت کرده است یا خیر، از تاخیر استفاده می‌کنیم. هنوز هم هست (ضربه بزنید). برای به دست آوردن فشار، فاصله بین دو لمس اولیه و نحوه تغییر آن در طول زمان را اندازه می‌گیریم.

در دنیای سه بعدی، شما باید تصمیم بگیرید که دوربین شما چگونه به عملکردهای ماوس در مقابل حرکت سوایپ واکنش نشان می دهد. یکی از راه های متداول اضافه کردن حرکت دوربین این است که حرکت ماوس را دنبال کنید. این را می توان با کنترل مستقیم با استفاده از موقعیت ماوس یا با حرکت دلتا (تغییر موقعیت) انجام داد. شما همیشه نمی خواهید در یک دستگاه تلفن همراه رفتاری مشابه یک مرورگر دسکتاپ داشته باشید. ما به طور گسترده آزمایش کردیم تا تصمیم بگیریم چه چیزی برای هر نسخه مناسب است.

وقتی با صفحه‌نمایش‌ها و صفحه‌های لمسی کوچک‌تر سروکار دارید، متوجه می‌شوید که انگشتان کاربر و گرافیک تعاملی رابط کاربری اغلب در مسیر چیزی است که می‌خواهید نشان دهید. این چیزی است که ما هنگام طراحی برنامه‌های بومی به آن عادت کرده‌ایم، اما قبلاً مجبور نبوده‌ایم با تجربه‌های وب به آن فکر کنیم. این یک چالش واقعی برای طراحان و طراحان UX است.

خلاصه

تجربه کلی ما از این پروژه این است که WebGL روی موبایل واقعاً خوب کار می کند، به خصوص در دستگاه های جدیدتر و پیشرفته. وقتی صحبت از عملکرد به میان می‌آید، به نظر می‌رسد تعداد چند ضلعی و اندازه بافت بیشتر بر زمان دانلود و مقداردهی اولیه تأثیر می‌گذارند و مواد، سایه‌زن‌ها و اندازه بوم WebGL مهم‌ترین بخش‌هایی هستند که برای عملکرد موبایل بهینه می‌شوند. با این حال، مجموع قطعاتی است که بر عملکرد تأثیر می گذارد، بنابراین هر کاری که می توانید برای بهینه سازی انجام دهید، شمارش می شود.

هدف قرار دادن دستگاه‌های تلفن همراه همچنین به این معنی است که شما باید به فکر کردن در مورد تعاملات لمسی عادت کنید و این فقط به اندازه پیکسل نیست، بلکه اندازه فیزیکی صفحه نمایش نیز هست. در برخی موارد مجبور بودیم دوربین سه بعدی را نزدیک‌تر کنیم تا ببینیم چه خبر است.

آزمایش راه اندازی شد و یک سفر فوق العاده بود. امیدوارم که شما از آن لذت ببرید!

میخواهی امتحانش کنی؟ سفر خود را به سرزمین میانه داشته باشید.