بررسی فنی و محتوایی داستان کودکانه ی تعاملی آنلاین «Oat the Goat» / اختصاصی گیکی زوم

کد خبر: 9586
«Oat the Goat» نام یک داستان تحت وب است که وزارت آموزش نیوزلند سفارش ساخت آن را به شرکت Assembly Ltd داده است. این بازی توانسته جوائز مهمی را در زمینه طراحی و داستان کسب کند. در این مقاله میخواهیم کمی به بررسی فنی این داستان بپردازیم و ابزارهای استفاده شده در آن را بیشتر بررسی کنیم.
گیکی زوم

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

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

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

این داستان 12 دقیقه روایت گری با دو زبان دارد و 11 سکانس دارد که روایت گری همگام شده با زیر نویس ها همزمان داستان را نقل می کنند. مملو از افکت های صوتی است و موسیقی زیر زمینه ای دارد که ارکستر سمفونی ملی نیوزلند آن را نواخته است.

71fe28b1a60e6e5317611c486ff5416e.jpg

سکانس ها در کل متشکل از 8 کارکتر انیمیشنی هستند و هر سکانس تکسچر و انیمیشن خاص خود را دارد:

Concept sketches of Amos

 

توسعه دهنده شرکت Assembly می گوید: «ما بیش از 13 هزار خط کد تایپ اسکریپت زدیم و به اندازه کافی HTML و البته CSS تعاملی یا همان Responsive که بتوانیم ساختار کلی را شکل دهیم؛ ساختاری که منجر به این شد که مخاطبان بتوانند در طیف وسیعی از گوشی ها و تبلت ها و کامپیوترهای شخصی و مرورگرهای مختلف بر روی آن ها این داستان را ببینند.»

این توسعه دهنده در ادامه مراحل مختلف کار و  ابزارهایی که در طول کار از آنها استفاده کرده است را نام می برد:

  • ترجمه کلیه متون به دو زبان انگلیسی و مائوری (زبان دوم نیوزلند)
  • نوشته شده به زبان TypeScript (نوشتن کل پروژه در جاوا اسکریپت یک کابوس بود)
  • رندر شده به WebGL توسط three.js
  • مدلسازی سه بعدیو روکشی اشیاء با کمک نرم افزار های Maya و Blender با خروجی های glTF فرمت .glb
  • صداگذاری توسط WebAudio توسط قابلیت های HTML5 بوسیله ی Howler
  • و در نهایت میزبانی یک سایت کاملا استاتیک بر روی کلود های S3 و CloudFlare

در ادامه این توسعه دهنده می گوید:

از آنجاییکه داستان با دو زبان انگلیسی و مائوری روایت می شد ما در ابتدا دو دامنه ی oatthegoat.co.nz  را برای  انگلیسی و otitenanekoti.co.nz را برای مائوری انتخاب کردیم که البته هر دو توسط یک کد نمایش داده می شوند. از آنجاییکه اکثر اشیاء بین دو زبان مشترک بود مشکل زیادی برای load و unload کردن اشیا نداشتیم. علاوه بر این از طرف تیم محتوایی این تاکید شده بود که نمی خواهیم کوچکترین فرقی بین این دو زبان باشد تا کسی بگوید نسخه انگلیسی بهتر یا بدتر از مائوری بوده است.

برای نمایش سکانس ها و Load شدن اشیاء جدید برای آن سکانس نیاز به کتابخانه ای با فاکتورهای ویژه ای داشتیم؛ چراکه نمیخواستیم تمایم اشیاء همه سکانس ها در همان ابتدا Load شوند. انتخاب معماری درست وقتی که با چنین سیستم هایی تابحال درگیر نشده باشید و یا در این scale کار نکرده باشید همیشه یک ریسک است.

در نهایت با کمک یک کلاس با نام StateMachine این مشکل را حل کردیم و توانستیم منابع را بصورت async در سکانس ها load کنیم. البته در این میان هندل کردن Play/Pause های بین داستان نیز پیچیدگی های خاص خود را داشت.

class Scene010States extends StateMachine {
    entry = this.intro;

    async intro() {
        this.sound.onceThenLoop(Layer.Music, "intro", "firstLoop");

        const vo = this.vo("vo_title");
        const anim = this.playSegment("intro")

        await Promise.all([vo, anim])

        this.advanceTo(this.waitClick);
    }

    async waitClick() {
        await this.getClick();
        this.advanceTo(this.outro);
    }

    async outro() {
        await this.playSegment("outro");
        this.scene.event(SceneEvent.End);
    }
}

برای خلق صحنه های سکانس، کار زیاد سخت نبود چراکه میخواستیم همه چیز مثل کتابهای بچه ها باشد و بنابراین همه چیز vector مانند بود. برای ایجاد این محیط لایه های اشیاء را با تکسچر هایی پر کردیم و برای نمایش سایه ها و نورها از اسیاء شفاف زیادی استفاده کردیم. و صدالبته که بر روی  لایه ها از خصیصه هایی نطیر depthWrite و depthTest بسیاری استفاده کردیم که حجم را در سکانس ها به خوبی نمایش دهیم.

برای اینکه بتوانم به نورها حجم بدهم که پخته تر به نظر برسند مجبور شدم دروس هندسه خود را دوباره مرور کنم و به شدت انها را به کار گیری کنم.

خروجی مدل های سه بعدی

یکی دیگر از کلیدی ترین کارهایی که می بایست انجام میدادیم انتقال یک کارکتر سه بعدی روکش شده از blender به مرورگر بود. پیش از این دستاوردهایی برای این کار داشتیم. در آن زمان این کار را با تبدیل فایل های .fbx بوسیله ی زبان پایتون به Three.js و با فرمت .json و فایل های Collanda با پسوند .dae انجام دادیم.

اما برای این پروژه زیاد نتیجه ی کار خوب به نظر نمی رسید بنابراین سراغ یه پروژه ی جالب با نام glTF رفتیم. با کمک این کتابخانه کل آبجکت و متعلقاتش در یک فایل با پسوند .glb ذخیره می شدند و کیفیت خوب خروجی Relatime  آن روی مرورگر نمایانگر آن بود. در انتها با gzip کردن تمامی فایل های glb توانستیم 25 درصد در پهنای باند صرفه جویی کنیم.

کتابخانه Load فایل های .glb در tree.js زیاد جالب نبود! بنابراین مجبور شدیم یک سری تغییرات دستی ای در آن بدهیم که کمترین میزان افت در load فریم ها و اشیاء وابسته ی آن را داشته باشیم. بعدها فهمیدیم که با جداسازی فایل های .glb به ازای هر کارکتر در هر سکانس می توانیم نتیجه ی بهتری بگیریم.

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

کارآیی و اشتراک داده

گاهی برخی کارکتر ها 120 استخوان داشتند و با وجود اینکه گاهی 8 کارکتر در صفحه داشتیم در برخی سکانس ها.. بنابراین متوجه شدیم که CPU زیادی صرف load کردن و حرکت دادن این استخوان بندی ها شده است. در اینجا و با توجه به اینکه انیمیشن های ما فریم به فریم نبودند توانستیم با یک آپدیت به ازای هر 12.5 ثانیه این مشکل را حل کنیم.

توی سکانس بره ها، تمامی بره ها استخوان بندی و رنگ بندی یکسانی داشتند و منطقی نبود که فایل های متفاوتی برای آنها بارگیری کنیم! بنا براین تابع cloneGLTF این وسط ما را نجات داد و توانستیم با کپی از مدل اولیه تنها انیمیشن های آنها را تغییر دهیم و سکانس را خروجی بگیریم.

صداگذاری و صداهای بسیار!

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

this.sound.onceThenLoop(
    Layer.Music, "sound", "loopSound",
    {fadeOut: 1}, // immediate fade specs
    {cross: 0.5} // fade specs for transition into "loopSound"
);
this.sound.loop(Layer.SFX, "atmos");

 


افزودن دیدگاه جدید

  • دیدگاه های ارسال شده توسط شما، پس از تایید در وب سایت منتشر خواهد شد.
  • پیام هایی که حاوی تهمت یا افترا باشد منتشر نخواهد شد.
  • پیام هایی که به غیر از زبان فارسی یا غیر مرتبط با خبر باشد منتشر نخواهد شد.