Lessons we learned while getting Wonderball Heroes on WebGL using Unity 5.
The slides share our challenges, optimizations made and general tips for working with Unity and WebGL.
9. New Version (Unity 5)
- Auto Update - Most of the heavy lifting
- Animator - Changes to “Apply Root Motion”
- AudioSource - new “Spatial Blend”
- Check Plugins for compatibility with Unity 5 !
10. New Platform (WebGL)
Encountered 2 WebGL related bugs:
- No WWW error reporting
- AudioSource.mute doesn’t work
… Already Fixed in Unity 5.1
11. New Compiler (IL2CPP)
- New & Innovative Tech → Bugs
- Issues with JSON.NET
○ Received a “street version” from the publisher
(nebraskadev@gmail.com)
○ No support for generic arrays (Fixed in 5.0.1)
- Check Plugins for compatibility with IL2CPP !!
12. Developing for WebGL
- Minimal code changes from WebPlayer → WebGL
- Build Settings
o Optimization Level
- Player Settings
o Browser Memory
o Cache
o Enabling Exceptions
o WebGLTemplates
13. Developing for WebGL
- Debug HELL (JavaScript Console FTW)
- Compression (gzip)
- Full Screen button - need workarounds
- Mixing domains (CORS)
- New Native Plugin = “.jslib”
14. Our Dev Process
- #if (UNITY_WEBPLAYER || UNITY_WEBGL)
- JS Wrapper for FB SDK
- Test as much as possible in editor
15. Our Dev Process #2
- Local Web Server (quick updates)
- Redirection to two versions
o Chrome → WebGL
o Other Browsers → WebPlayer
21. Build Output
Game data
(all assets, scenes, etc)
Code (converted to js)
Binary image for heap
memory (Emscripten)
Embeds the game
content
Supporting files for
initialization
Same as this folder, only
compressed (gzip)
22. Streaming In WebGL
- No official solution out of the box, but...
- ...Using an editor script by Unity Developer
(Thanks @jonasechterhoff !)
o Shared in the Unity Beta Group
- Implemented as a [PostProcessBuild] step
23. In a Streamed Web Player:
“The index of the first level that will have
access to all assets under Resources”
http://docs.unity3d.com/Manual/class-PlayerSettingsWeb.html
First Streamed Level
24. Streaming WebGL Solution
1. Split “game.data” and “fileloader.js” to smaller files (one
per scene)
2. Add Resources to .data file of “First Streamed Level”
3. Combine all the new “fileloader” files into one
4. Change the code to run the game after first scene
downloaded
5. Downloads scenes one by one
25. Custom Build Script
- Removes unneeded scenes
- Removes resources from the build
- Removes texture usage & downloads them
at runtime
26. Final Results
- The game runs smoothly !!
- Final build size: 28 MB
- New users - can play after 3 MB
- No need to install any plugin
- Loading time is longer than WebPlayer
27. Thank You !
Lior Tal
liortal53@gmail.com
http://www.tallior.com
Oren De-Panther Weizman
depanther@gmail.com
http://de-panther.com/
29. Tools
● Browser Developer Tools (Chrome, Firefox)
o JS console, Network requests
● Charles (web proxy)
o View network requests + responses
● NGINX - lightweight, portable web server
o Setup a local test environment
This session tells the dev story of our game - Wonderball Heroes, and how we released it for WebGL (after having mobile versions as well as a WebPlayer based version).
We plan to talk about the challenges we faced with working on the new platform, how we handled them and what optimizations we used to get the game down from 300mb to 28mb (its final size).
Quick intro - this is us at Moon Active (photo taken during April 2015).
We are 11 in total, located in Tel Aviv and we’re focusing on development of “Wonderball Heroes”.
Few words about our game - Wonderball Heroes:
It’s a casual, 2D addictive game where you must destroy all red pegs in a level to move on.
The game has a very colorful visual style with lots of different characters, powerups and boosters, with over 250 levels already available.
The game is already released on multiple platforms - iOS, Android and the web (Facebook canvas).
On mobile we have over 2 Million downloads in total, and the game was featured multiple times on both stores (iOS/Android).
Without getting too deep into technical stuff, this is the definition of WebGL we’ll use.
Why should you care about this platform ?
Chrome is a very popular browser with a big market share. Since version 42 (released last week), NPAPI is not support anymore (thats the tech that WebPlayer is based on), rendering all existing WebPlayer non-working.
Discuss Unity’s solution for WebGL.
It’s out since Version 5 (released on March 2015), still marked as “Preview” which means it’s pretty early stuff, although it had lots of beta cycles and many efforts being put into it.
Officially, Unity claims to support Firefox & Chrome, although YMMV so test out other browsers for their support.
Lastly, WebGL has a few limitations (see link in the end for complete listing).
Briefly mention the new build process:
starting with source code, we compile it into IL (same as before, using age-old Mono environment).
Then the new IL2CPP process converts the compiled IL into C++.
Last, Emscripten generates asm.js from the c++ code, allowing it to run in the browser.
Challenges with such a project - upgrading from Unity 4 → Unity 5, new platform (bugs, knowledge gaps), new compiler (innovative new tech → bugs)
אורן:נתחיל בחוויית השדרוג ליוניטי 5.
Upgrade to Unity 5 was a “fairly smooth” process. There’s an auto update tool that will convert code (both source and compiled) into the “new API”.
Sometimes it won’t be able to do its job so you have to manually do some stuff (literarrly change 2 lines of code for us).
Other suff we faced was changes in the way some components work, for example - Animator and AudioSource.
Important note - if you use 3rd party plugins - verify their support for Unity 5 !!! please !!!
Since this is a new platform we encountered a few bugs during development.
We won’t elaborate since both were fixed.
I urge you all to report all bugs to Unity since they are very responsive, especially when it comes to new platforms. Both issues we reported are already resolved for Unity 5.1.
New compiler → new bugs.
Good news -- since it’s “the future” of unity scripting, they fix many bugs for il2cpp.
We faced issues with JSON.NET, but contacted the developer (see email) and we received a version that supported it (since it wasn’t released to the asset store).
Again, like we said before - Check for plugin compatibility with IL2CPP !
Stuff to consider when developing a game for WebGL:
Luckily we had the WebPlayer version. The code changes moving to WebGL were minimal.
There are some build and player settings that allow you to control optimizations and behaviour of the game. Check out the docs for more info on that.
Also, you can define (like in WebPlayer) your own template with customized look of the web page that hosts the game contents.
ליאור:
דיבאגינג - לא קיים כרגע !!! מזל שיש את הג'אווהסקריפט קונסול (printf debugging).
כיווץ - יוניטי מייצר גירסת ג'יזיפ של הקבצים, צריך רק לדאוג שהשרת ידע להפנות לקבצים המכווצים. אגב *זה* לבד הוריד את הפרוייקט מ300 מגה ל50 מגה!
כפתור מסך מלא - יש בעיה עם כפתור מסך מלא מתוך הנגן של יוניטי, בגלל מגבלות אבטחה של דפדפנים, הקריאה צריכה להיות רק בהרמת אצבע מהעכבר, אבל יוניטי לא קורא לזה ישירות בגלל הלולאת משחק שלו. יש פתרון.
ערבוב דומיינים - הגדרות שצריך לדאוג להן בצד שרת אם רוצים לטעון קבצים משרת בדומיין נפרד - זה לא קשור ליוניטי, לכן לא נרחיב. המנגנון פה עובד אחרת מ webplayer (שם צריך crossdomain) ופה צריך להגדיר cors שזה headers שהשרת צריך להחזיר.
פלאגאינז נייטיב, משתמשים בסיומת JSLIB בשביל קבצי ג'אווהסקריפט שמתממשקים עם הדפדפן. אפשר גם לטעון פלאגאינים של C/C++
אורן:קצת על תהליך העבודה שלנו:
מכיוון שכבר עבדנו על גירסת וובפלייר בעבר, לא היינו צריכים לעבוד על UI חדש.
בעיקר עברנו על הקוד והחלפנו במקומות הרלוונטים את הבדיקה של הוובפלייר, שתתאים גם לוובג'יאל.
במקרה של הJS SDK של פייסבוק, כבר עטפנו אותו בשביל הוובפלייר, בגלל שהSDK הרגיל שלהם עשה לנו בעיות. כשהוספנו לבדיקה האם מדובר בוובג'יאל, הקוד פשוט עבד, ולא היינו צריכים לעשות שינויים נוספים בחלק של יוניטי.
השתמשנו בפקודות של ExternalCall ,ExternalEval בתוך יוניטי, שעובדות באותה צורה ומאפשרות תקשורת בין הקוד לדברים שמוגדרים ב js.
מהעמוד עצמו השתמשנו בSendMessage, שעובדת בצורה דומה (רק נמצאת תחת אלמנט שונה). כאן היינו צריכים לשנות קריאה אחת.
בנוסף, משהו שמאוד עזר לנו - זה לדאוג שהפלואו של המשחק יתנהג כמה שיותר זהה בעורך. כך שבאגים שרלוונטים ליוניטי5 באופן כללי, מתגלים בעורך עצמו.
אורן:עוד על תהליך העבודה שלנו, והפעם בהקשר של שרתים.
הבילדים הראשוניים היו בגודל 300 מגה, כדי שלא נצטרך כל פעם להעלות את המשחק לשרת מרוחק לצורך בדיקה, אנחנו מריצים שרתים מקומיים על המחשבים שלנו, ואז בודקים איך המשחק עובד באפליקציית פייסבוק פרטית. חוסך המון זמן.
מכיוון שאנחנו מוציאים את גירסת הוובג'יאל רק לכרום, אנחנו עושים בדיקה מה הדפדפן של המשתמש, ולפי זה מפנים אותו לגירסת המשחק המתאימה. שימושי למי שרוצה לתמוך בכמה גרסאות במקביל (כמונו).
ליאור:אז אחרי שדיברנו על תהליך השדרוג, בעיות ספציפיות שחווינו ומה עשינו כדי לפתור אותן, הגענו למצב שאפשר לבנות את המשחק והוא גם רץ בצורה די חלקה.
אבל…. <הפוגה דרמטית>
הבילד של המשחק היה בגודל 300 מגה (בערך), מה שאומר שכדי לשחק צריך להוריד 300 מגה ואז לחכות שייטען הכל לזכרון של הדפדפן (מחזיקים אצבעות שזה לא יקרוס תוך כדי).
כל זה יכול לקחת עשרות דקות (!!) מדדתי פעם 28 דקות עד שאפשר היה לשחק.
בשלב הזה החלטנו שהאופטימיזציות הכי חשובות לנו להתמקד בהן זה הקטנה של גודל המשחק.
ליאור:״כלי״ קטן שעזר לנו להבין מה תורם לגודל המשחק הסופי הוא ה editor log. זה רלוונטי לכל פלטפורמה, לא רק ל webgl.
אחרי שמסיימים לבנות את המשחק, ב editor log מופיע סיכום הגודל הסופי של ה build, עם אחוזים עבור כל סוג אובייקט (כמו בתמונה), ואח״כ מופיע גם פירוט עבור כל קובץ שהיה חלק מהבילד (בסדר גודל יורד, כלומר הassets הכי כבדים יופיעו ראשונים ברשימה).
זה עוזר להבין מה ה assets הכי גדולים במשחק, וגם לפעמים יכול לעזור לעלות על assets שלא אמורים להיות חלק מהבילד אבל בכל זאת מופיעים שם.
ליאור:האופטימיזציה הכי משמעותית בצמצום גודל הבילד הייתה שימוש ב compression.
אם לא בחרנו ב development build, יוניטי מייצר תיקיית compressed שמכילה את המשחק בצורה דחוסה. זה לבד מצמצם את הגודל משמעותית.
חוץ מזה, יוניטי מייצר קובץ htaccess שנתמך על ידי שרתים מסוימים ואומר להם אוטומטית ״להשתמש בקבצים הדחוסים״.
אם השרת לא תומך בזה צריך לעשות כמה שינויים לשמות של קבצים לפני שמעלים אותם.
עם compression, הגודל של המשחק שלנו צומצם משמעותית מ300 מגה לאיזור ה 50 מגה.
ליאור:רמת האופטימיזציה זו אחת האפשרויות ב player settings. האפשרות הזו קובעת מה רמת האופטימיזציה שאנחנו רוצים (כמובן שהכי גבוהה !!!)
צריך לזכור שככל שיש יותר אופטימיזציות, הבילד יקח יותר זמן (אצלנו מדדנו כמעט פי 2 הפרש בין slow ל fastest).
עבור גרסה שמשחררים החוצה (הגרסה הסופית של המשחק) זה פחות מעניין - ברור שנרצה הכי הרבה אופטימיזציות, אבל כשבונים כמה גרסאות ביום זה משהו שכדאי לקחת בחשבון (אפשר לעשות קפה בינתיים או לראות סרטונים של חתולים ביוטיוב).
*פרט טכני למי שרוצה להכנס יותר לעומק - הפרמטר הזה שולט ישירות באיזה פרמטר של אופטימיזציה נשלח לקומפיילר של emscripten (לינק לתיעוד שלהם שאפשר לקרוא מה זה עושה בדיוק).
ליאור:עוד אופטימיזציה שאפשר לשלוט בה היא exception support.
תמיכה ב exceptions גם מגדילה את הגודל של המשחק (צריך לייצר עוד קוד שמטפל בהם) וגם משפיעה ישירות על הביצועים (צריך להוסיף קוד שבודק האם משהו הוא null או האם צריך לזרוק exception.
יש 3 אפשרויות שונות לשליטה ב exceptions החלט מלבטל אותם לגמרי, ועד לתמיכה בכל סוגי ה exceptions.
ללא - אין כל תמיכה ב exceptions. חשוב מאודדדדדד לזכור שאם יש לכם קוד שכתוב בצורה מסוימת שמסתמכת על exceptions , הקוד הזה לא יהיה קיים פה, ורוב הסיכויים שהמשחק יתרסק ולא יעבוד בכלל. ככה היה במקרה שלנו.
הרמה הבאה היא explicitly thrown - זה אומר כל מקום בקוד שבו קוראים ל throw (בין אם בקוד שלנו או בקוד של ספריות). זה אומר שרוב ה exceptions נתמכים. משלמים על זה כפי שציינתי למעלה - גם בגודל וגם קצת בביצועים, אבל לא משהו שהשפיע עלינו בצורה משמעותית, ולכן זו האפשרות שבחרנו.
האפשרות האחרונה היא תמיכה מלאה ב exceptions, גם כאלו שמיוצרים על ידי ה runtime בשבילנו כמו null reference ו array out of bounds. זה היה over kill מבחינתנו וויתרנו על זה.
אורן:כשבונים ל webgl, מקבלים תיקיה שתיראה קרוב מאוד למה שמוצג כאן (שמות הקבצים תלויים בשם שבחרנו, אני קראתי למשחק webgl).
בשקף פה אפשר לראות את החלקים המרכזיים:
דף index - נקודת המוצא. אל הדף הזה גולשים כדי להתחיל את טעינת המשחק.
כל ה data של המשחק ארוז בתוך קובץ אחד (ענקי) עם סיומת data
הקוד המתורגם (ל js) יוצא בקובץ אחד (מכיל את *כל* הקוד, כולל mscorlib, הקוד של המנוע ו user code, כלומר - קוד המשחק שאנחנו כותבים). לא מומלץ לנסות לקרוא את הקוד הזה… בלי שקית הקאה
קובץ בינארי עם סיומת mem עבור אתחול של statics (זה קובץ של emscripten, מידע נוסף כאן: http://kripken.github.io/emscripten-site/docs/tools_reference/emcc.html#emcc-memory-init-file)
עוד כמה js קטנים בשביל תמיכה עבור העלייה והאתחול
תיקיה של compressed שמכילה אותו דבר רק מקומפרס (gzip)
אורן:הדבר הבא שרצינו לטפל בו כחלק מהאופטימיזציות הוא סטרימינג.
בתאכלס, אין פתרון רשמי.
כשהתחלנו לעבוד על גירסת הוובג'יאל, אחד המפתחים של unity כתב סקריפט שעושה משהו דומה ושיתף אותו עם כולם בקבוצת הבטא.
הקוד עבר כמה שיפוצים קלים שלנו, ודאגנו שהוא יעבוד בצורה קרובה יותר לאיך שזה עובד בוובפלייר.
אורן:כדי להסביר קצת יותר לעומק איך סטרימינג עובד, צריך להזכיר את המושג של first streamed level.
בגירסת הוובפלייר של יוניטי, יש אפשרות לעשות סטרימינג לסצנות.
במקום להוריד את כל המשחק, ורק אז לטעון אותו, אפשר להתחיל את המשחק כבר כשהסצנה הראשונה סיימה לרדת, ובינתיים לאפשר לשחקנים לעשות פעולות שונות שלא מצריכות להוריד את המשחק כולו.
כשמשתמשים באפשרות הזו, אפשר לקבוע בנוסף עם איזו סצנה האסטים שמקושרים לתיקיית הרסורסס* ירדו.
ב wonderball heroes רצינו ששחקנים חדשים יוכלו כמה שיותר מהר להכנס למצב טוטוריאל - אז אנחנו דואגים שקודם ירדו האסטים המינימליים שצריך בשביל השלב הראשון, ותוך כדי שהיוזר משחק, שאר הקבצים ממשיכים לרדת ברקע.
עדיין יש כמה אסטים שאנחנו צריכים מתיקיית הרסורסס, אז אנחנו דואגים להצביע אליהם כבר בסצנה הראשונה, כך שהם ירדו יחד איתה.
אורן:אז איך בתאכלס זה עובד?
זוכרים שדיברנו על מה מוציא בילד של וובג'יאל?
יש קובץ של דטא, ויש קובץ שנקרא fileloader.
הקובץ של הלואדר, בעצם טוען את קובץ הדטא, ולאחר שהוא מסיים לרדת, טוען את המשחק.
הסקריפט של הסטרימינג, בונה קובץ דטא לכל סצנה.
בסצנה הראשונה הוא דואג לשים גם את הקבצים שקשורים לסצנה הראשונה, וגם קבצים בסיסיים מבחינת הפרוייקט.(שיידרים דיפולטיביים למשל, בלעדיהם המשחק לא ירוץ)
ובסצנה שבחרנו בתור פירסט סטרימד, הוא שם גם את התוכן של תיקיית הריסורסס.
בנוסף, הוא גם מייצר fileloader לכל סצנה.
בסוף, הוא מאחד את כל קבצי ה fileloader לקובץ אחד, כאשר הוא קורא לפקודה שטוענת את המשחק, כבר בסיום ההורדה של הסצנה הראשונה.
אחר כך הוא ממשיך להוריד את קבצי הדטא של שאר הסצנות.
זה לא מוריד את גודל הבילד, אבל זה גורם לכך שלפחות יוזרים חדשים, יהיו צריכים לחכות פחות זמן בשביל לשחק.
ליאור:האופטימיזציה האחרונה שרצינו לעשות הייתה להפטר מדברים מיותרים שלא צריכים להיות חלק מה build. למה לנו ״לשלם״ על משהו שאין בו שימוש ?
לדוגמה:
סצינות שרלוונטיות רק למובייל (rate us)
בגרסאות המובייל אנחנו מכניסים לגרסה את כל ה data של השלבים (בפורמט json) כדי שאפשר יהיה לשחק במצב offline.
ב web אין צורך כי יש קישוריות רוב הזמן - את השלבים מורידים מהשרת.
בניתי כלי שמאפשר לתאר ״איזה דברים לא צריכים להכנס לבילד״, כמו סצנות ותיקיות תחת resources.
בנוסף, במצבים מסוימים החלטנו שטקסטורות מסוימות ייטענו ב runtime מהשרת, במקום לשמור אותן כחלק מה data של המשחק (תנסו לנחש איפה הן, אני די בטוח לא תשימו לב שהן יורדות דינמית בזמן ריצה !)
ליאור:
המשחק רץ חלק,
הבילד הסופי הוא 28 מגה.
יוזרים חדשים נכנסים למשחק מהר יותר.
אין צורך בפלאג אין.
*הדגמה של המשחק אם אפשר*
תודה על ההקשבה, אם יש שאלות - נשמח לענות.
אלו הפרטים שלנו, תרגישו חופשי ליצור קשר אם יש שאלות נוספות שעולות !
Got any questions? Feel free to drop an email !
This page lists tools we used during development
This page contains links to resources that were previously mentioned regarding Unity and WebGL.