Unity 5 and WebGL porting guide

So, you have been working on Mobile games for a while using Unity, and probably heard that with Unity 5 it was possible to port your games for the Web sans Unity Web Player.

Great, right? Well, yeah, but there are a few things to know before making your game work with WebGL.

Let’s start by answering the following question:

Why is WebGL so important for game development?

For starters the Web has always been a strong platform with an established user base of about everyone. At the time of writing this, with WebGL you can target almost every desktop platform that has a relatively modern Web browser without the need of installing any additional plugins.

But the most important part of building for the Web is that you control the distribution. Forget about sending your build for review, or uploading your package through a console and waiting hours to test your build. With web technologies it has always been a matter of uploading your files via FTP and seeing the changes reflected in real time.

This is clearly and advantage when you are iterating fast and want to test your latest build on an environment that resembles production.

Also important to mention is that Google Chrome has recently dropped support for NPAPI, which means plugin based games (like those using Unity Web Player) won’t longer run on that Browser. Other browsers may follow in this decision in the future, so it’s better to be prepared.

But enough for an introduction, let’s move  on to the interesting stuff. As with any other platform, when you build your Unity 5 project for WebGL, there are a few things to take into consideration.

Here’s a handy list of issues we ran into when porting our first game from Android/iOS to Facebook Canvas (all this information can also be found on Facebook’s Developer Documentation and/or Unity docs):

First things first: Unsupported Namespaces

  • Everything in the System.Net namespace (particularly System.Net.Sockets) are non-functional in WebGL.

According to Unity docs, this is because:

Due to security implications, JavaScript code does not have direct access to IP Sockets to implement network connectivity.

If you need to use Networking in WebGL, you currently have the options to use the WWW or UnityWebRequest classes in Unity or the new Unity Networking features. It’ll bring some headaches because of CORS (Cross-origin resource sharing), which basically means you won’t be able to access any other URL that is not in the same domain as the one where your game is hosted. Alternatively, you can also shoot yourself in the foot… mhhh, I mean, if you own the server that you are trying to access, you can add some headers to your Apache configuration to enable CORS.

This is probably one of the biggest headaches you’ll have to solve in order to port your game to WebGL (if you use asset bundles or need to download resources from an external server).

  • Just in case you were wondering:

The same applies to Unity’s old UnityEngine.Network* classes, which are not available when building for WebGL.

Let’s make it clear: this is not Unity’s fault, at all, it’s just the way the Web is designed to work.

  • WebGL is an AOT (ahead of time) platform, so it does not allow dynamic generation of code using System.Reflection.Emit.
  • Basically, anything in the System.Threading namespace is not supported.

So far we’ve dealt with unsupported namespaces. For us, the way to work around these issues was to use Visual Studio 2015′s search tools to do a search in our Entire Solution for files where those namespaces were being imported (using namespace blabla). Afterwards we would add a pre-processor directive for !UNITY_WEBGL, and find a way to fix all the errors that came up when the offending namespaces where removed (with further uses if UNITY_WEBGL).

Example:

#if !UNITY_WEBGL
using System.Threading;
#endif

Second: Asset bundles

There are some considerations when using AssetBundles on the WebGL platform:

  • When you are using class types in your AssetBundle which are not used in your main build, this can cause Unity to strip the code for those classes from the build, which will then cause it to fail when trying to load assets from the AssetBundle. Information about how to deal with this issue can be found here.
  • Since WebGL does not support threading, and since http downloads will only become available when finished, Unity WebGL builds need to decompress AssetBundle data on the main thread when the download is done, thus blocking the main thread. To avoid this interruption, you may want to avoid using the default LZMA Format for your AssetBundles, and compress using LZ4 instead, which is decompressed very efficiently on-demand. If you need smaller compression sizes then LZ4 delivers, you can configure your web server to gzip-compress the files on the http protocol level (on top of LZ4 compression).

Third: Building and Testing

This is probably one of the worse parts of building for WebGL. It’s not clear what each setting does to the final build or how you are supposed to test and debug your game. Read through this section to find out and thank me later.

  • You can view your WebGL player directly in most browsers by simply opening the index.html file. For security reasons, Chrome places restrictions on scripts opened from local file:// URLs, so this technique will not work. If you use Unity’s Build & Run command (menu: File > Build & Run) then the file will be temporarily hosted in a local web server and opened from a localhost URL (this avoids the security restrictions). You can also run Chrome with the –disable-web-security command line option to enable it to load content from file: urls.

In our case, we created a simple *.bat script with this single line (edit accordingly if you installed Chrome in another directory):

“C:\Program Files (x86)\Google\Chrome\Application\chrome.exe” –disable-web-security

At least in Windows, you’ll be able to have the script in your desktop and double-click it to launch a Chrome window that will allow CORS. We found this is the simplest way to test builds on the fly and used this flow during our process. It even works for builds that are hosted on a server.

  • Tips for building:

These are quoted word to word from Unity docs, but we marked those that we’ve found have the most effect in the final build size and performance.

  1. Specify the “Crunch” texture compression format for all your compressed textures in the Texture Importer (Depends on your project).
  2. Don’t deploy Development builds, they are not compressed or Minified and so have much larger file sizes (VERY IMPORTANT for build size).
  3. Set Optimization Level to “Fastest” (VERY IMPORTANT for performance).
  4. Set “Enable Exceptions” in Player Settings to “None” if you don’t need exceptions in your build (VERY IMPORTANT for performance).
  5. Enable “Strip Engine Code” in Player Settings (VERY IMPORTANT for build size).
  6. Take care when using third-party managed dlls, as those may drag in a lot of dependencies and increase the emitted code size significantly (Didn’t affect us).

Update: Settings exceptions to “None” could bring issues if your code depends on Exceptions in order to execute properly (for example, when calling a server). You’ll have to test this and find out what works best for your game.

  • Profiling WebGL

You can use the Unity profiler on WebGL, just like on any other platform. One important distinction is that you cannot attach to running players in WebGL, though, as WebGL uses WebSockets for communication, which will not allow incoming connections on the browser side. Instead, you need to use the “Autoconnect profiler” checkbox in the build settings. Note also that draw calls cannot currently be profiled for WebGL.

  • Debugging WebGL

Good luck with this. Currently, it’s not possible to debug WebGL builds. In our workflow, we went old school and added Debug.Log calls in the places where we were having problems. Every browser has it’s own developer console that is usually accessed by pressing F12.

Combining this with a Development build with exceptions enabled is your best bet.

  • Browser compatibility

Up-to date information for each browser and version can be found here.

That said, it’s also good to consider some of the older browsers that don’t offer good/any WebGL compatibility. In those cases, (like Internet Explorer), it’s best to have a Unity Web Player build and load one or the other depending on Browser support.

On the other hand, Google Chrome completely ditched Unity Web Player support, so the only option there is to go with WebGL. All in all, we recommend using WebGL for every browser except IE.

Something to note is that it works surprisingly well on Edge, Chrome is Okay and in my experience Firefox has been the worst (more on that in the next point).

  • Memory!

This is another big problem. The browser will complain if you assign too much memory, but it will crash if you don’t assign enough. So basically, it’s an exercise of finding the right amount so the browser will allow it to run, but at the same time won’t crash during run-time.

There are two ways in which you can change the amount of memory the game will require at startup (this is the only time when a game gets assigned memory in WebGL, so you need to make sure it’ll be enough to fit the whole game plus any assets you’ll need to download during run-time). The first method is using Player Settings in Unity Editor. Under WebGL settings, select Publishing Settings and enter the memory size. Default is 256MB. In our case, it was enough for the Release build (around 90MB), but not for the Development build (around 300 MB).

This is where Firefox has been the worst for us. Every other browser had no problem in assigning 512 MB of memory for a Development build to run, except Firefox. That means (in our case) we were not able to test Development builds. It’s not that bad, but if your game is larger than ours, it could come to a case where not even your Release build will run.

We are still doing some research about this point in particular and will update when there are new findings.

Update: as someone mentioned on a Reddit comment, this is because Firefox is already using too much memory on other tabs, not because it can’t assign 512MB. Closing and re-opening Firefox does the trick.

The second way to change your memory size is by opening the index.html file generated by Unity, and navigating to the piece of code shown below:

memoryChange the TOTAL_MEMORY value to reflect the amount of memory your game needs (in bytes). For example, 268435456 bytes for 256MB.

Fourth: Facebook Canvas

  • Before starting watch this video. It’ll give you an overview of the whole process.
  • If you want your FB Login to work like it was working on iOS/Android, you’ll need to go to  Advanced settings in your FB app and activate Web OAuth Login.

oauthsettingsThen, you need to add a Valid OAuth redirect URI (this can be the same as your secure URL), or Facebook won’t allow you to save your changes.

  • Canvas.Pay: the guys at Bigfoot Games did a whole post that talks about this process, you can find it here.

Pay special attention to the steps to configure your *.html files for products you want to sell.

Once you move back to Unity, something to take into account is that FB SDK has changed a bit since they published their post, and there’s no longer a response.Text field in the result.

Refer to Facebook Upgrading guide where it explains about typed responses: https://developers.facebook.com/docs/unity/upgrading-7.x

To sum it up a bit:

Method callbacks are now typed and have distinct result classes. For example: FB.API will return an IGraphResult and FB.Canvas.Pay will return an IPayResult

In SDK 7.X  you’ll have to replace all the code that deserializes response.Text and then accesses “status” field, to the way more simple:

 response.ResultDictionary["status"]

This is the C# code that goes in the method that handles the click event on the Buy button:

FB.Canvas.Pay(product:"https://your_url_here.html", //If you have more than one IAP, you would pass the different URLs as a parameter
             action: "purchaseitem",
             quantity: 1,
             callback: PayCallback);

The PayCallback:

void PayCallback(IPayResult response)
{
    Debug.Log("PayCallback");

    if (response == null)
    {
        Debug.Log("response is null");
        return;
    }

    Debug.Log("Response Cancelled: " + response.Cancelled);
    if (!response.Cancelled)
    {
       if (Convert.ToString(response.ResultDictionary["status"]) == "completed")
       {
          Debug.Log("Status was 'Completed'");
          HandleYourItemChange(someQuantity); //Give items to the player
       }
       else
       {
         Debug.Log("Payment failed: " + response.Error );
         //Payment Failed
       }
    }
}

Something else the post doesn’t mention is that you need to access your App configuration page in Facebook’s developer portal and create a new company under the Canvas Payments option on the left menu. Follow the steps and you’ll be up and running in no time.

Facebook official docs about that matter can be found here.

Useful resources

Unity Docs: http://docs.unity3d.com/Manual/webgl.html

Facebook Docs: https://developers.facebook.com/docs/unity/getting-started/canvas

This is as deep as our integration went. For any other tips, comments or suggestions about how to update this guide, please leave your comment below or send us an email to play@trickgs.com

This entry was posted in Development and tagged , , , , . Bookmark the permalink.

7 Responses to Unity 5 and WebGL porting guide

  1. I’m enjoying the opportunity to read articles about the conversion of existing games to Unity 5.

    Egowall is not (yet) a mobile game, but we’ve had quite an experience moving to Unity 5 and WebGL. It has not been easy and we’re not all the way there yet. We’ve written a number of posts about what we’ve observed.

    If you’re interested, the first article in the series is at http://blog.egowall.com/blog/2016/2/19/egowall-and-webgl-part-one; we welcome any comments or feedback.

    Thanks!

  2. Sreejit says:

    Hellol,

    I am working on a multiplayer game for WebGL platform and i am using (http://lockstep.io/ – SocketIo & node.js server ) for handling the game.

    I am facing these issues as shown in the attached image link.

    I have no idea about what these errors state?

    I don’t think that the issue is from my coding side as my multiplayer game runs perfectly in standalone builds with more than 4 instances of the standalone build.

    But it does not even open in my WebGL build.

    Does WebGL does not support threading??

    Please guide.
    What are these errors all about?
    Can these issues be solved??
    or
    Do i have to change my whole project implementation?

    Thank you!

    http://i.imgur.com/wPAc6Fl.png

    • Leandro says:

      This looks like an exception within your code. If you are using Unity you can Enable exceptions and have a more detailed error log. WebGL is single-threaded, so no, it doesn’t support the System.Threading namespace. You’ll have to comment it out or add a conditional compiler and fix your code everywhere where you use it.

  3. Jesal says:

    Hello

    If I compile my application for webgl, can I upload from assetbundles, scenes that have scripts in c # and were not present in the compilation?

    Should I change scripts from C # to javascript, to work the scenes loaded at runtime from assetbundle?

  4. Tamars Jone says:

    Is it possible you deploy the WebGL in windows Azure? If it is possible is there a source or a tutorial that can help me in going about deploying my unity WebGL into Windows Azure..

    • Leandro says:

      Yes, definitely, Windows Azure is just a hosting provider with some (a lot) of extra features. It can do whatever all other servers do. Just make sure you add a Mime type for Unity files.

Leave a Reply

Your email address will not be published. Required fields are marked *


5 + eight =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>