Creating a desktop version of the mobile game has become a real research mission for the Krasnodar studio Plarium. In this article, we will describe how we switched to the WebGL technology when moving the Vikings: War of Clans project to a new platform.

Mastering WebGL
The game Vikings: War of Clans was created on the cross-platform engine Unity 3D, which allows you to run the application on more than 25 platforms: from a mobile device to the browser. We already had experience with Adobe Flash, but for this project we chose WebGL technology, and here's why:
- The business logic of an existing project could be moved almost unchanged.
- WebGL technology is compatible with almost all modern browsers and does not require additional plug-ins.
- Unity Technologies does not hide the fact that WebGL is a priority technology for them, and with each new version the engine progresses noticeably: documentation is improved, bugs are fixed, functionality is added.
It is worth noting that the developers of Unity provide high-quality technical support and extensive documentation. When working on the project, we received all the necessary information about the limitations for WebGL in the user manual and in the forums dedicated to this engine, so that you will not be left without help.
')
Translation difficulties
In order to build a WebGL build, the Unity engine performs a complex chain of actions: using Mono C # compiler, the game code is converted into intermediate (IL) code, and then using IL2CPP technology, the code in C ++ is obtained, which in turn gives us the WebGL project through the Emscripten compiler. As a result, not all .Net features provided by the Unity 3D engine are available in the final assembly. For example, multithreading is not supported, System.Net.Sockets does not work, etc.
To complete the transition to the web version, we had to fix a number of problems.
WebSockets implementation . For networking, a wrapper was written that, with minimal changes, allowed us to work with the server through web sockets. A simple example is available in the
Unity Asset Store .
Lack of multithreading . If in the mobile version, different processes were launched in parallel in separate threads and were not interrupted until they were completely completed, then the single-threading characteristic of the web version required a different approach in order to prevent application hangs. To do this, we transferred all actions to coroutine and thus achieved the distribution of the load over time.
Opening tabs . Unity transfers the recorded user actions to the buffer and transfers them during the internal frame update. In such a situation, the browser protection does not allow to open another window. The problem was solved by a small extension to the engine - a native browser plug-in, into which mouse events are directly thrown.
OpenURL: function(url){ window.open(Pointer_stringify(url)); }
So managed to achieve the opening of all the necessary windows. About creating plugins for WebGL is written in the
official documentation and in the
blog of developers .
Fonts . At some point, there was a need to refine the display of characters. In particular, the input of hieroglyphs should be done as follows: the user types in combinations of Latin letters, which are converted into Japanese, Chinese or Korean letters. In Unity itself, this method is not provided, but this functionality is in browsers. We implemented it using a separate plug-in. In addition, WebGL, unlike native applications of the same Flash, does not have access to system fonts. Therefore, we added them directly to the project, which, of course, increased the size of the assembly.
Texture quality settings . It took a lot of time to adjust the balance between the quality of texture compression and the size of the build. It was necessary to experimentally find out which textures are transferred from the mobile version, you can pinch, and which not. Gradually, we came to the optimal settings, which are now automatically set.
In our game there are several atlases for textures, and, as a rule, we use DXT1- and DXT5-compression (depending on the presence or absence of an alpha channel in them). The exceptions are two atlases: containing textures that are extremely bad at compressing (textures with a gradient, buttons with numbers or letters) and a satin for emoji symbols. In these cases, we use RGBA32.
To reduce the size of all other textures that are outside the atlases, the formats DXT1Crunched and DXT5Crunched are suitable. At the moment, we have redefined the OnPreprocessTexture method of AssetPostprocessor, which allowed us to automatically apply these settings to all textures in the project.
Third-party services . Be prepared for the fact that third-party libraries that are effectively used in a mobile game may not work correctly or even not work at all when building a WebGL project. In such a situation, the best way out is to find an alternative. For example, for authorization on Facebook, we additionally used the native JavaScript plugin. It allows you to identify the user already at the time of loading the page and obtain in advance the permissions (permissions) required for the game.
Moving from Unity 5.2 to 5.6 . During the development process, we needed to switch from Unity 5.2 to version 5.6, in which Unity Loader was added and several critical issues were fixed (for example, a build crash during a page reload). These changes greatly simplified the initialization of the application. You can see the download code for the builds of both versions below.
Unity 5.2:
var canvas = document.createElement("canvas"); canvas.style.width = "100%"; canvas.style.height = "100%"; canvas.addEventListener("contextmenu", function(e) { e.preventDefault() }), canvas.id = "canvas"; gameContainer.appendChild(canvas); if (consts.isDebug) { createScript('unity52/module_pre_debug.js?v=' + consts.appVersion); document.WebExistsGLCallback = function() { Module.postRun.push(unityHelper.postRun); createScript('Debug/Release/fileloader.js?v=' + consts.appVersion); createScript('unity52/module_post_debug.js?v=' + consts.appVersion); tracker.load(); }; createScript('unity52/UnityConfig.js?v=' + consts.appVersion); } else { createScript('unity52/module_pre.js?v=' + consts.appVersion); document.WebExistsGLCallback = function() { Module.postRun.push(unityHelper.postRun); createScript('Release/fileloader.js?v=' + consts.appVersion); createScript('unity52/module_post.js?v=' + consts.appVersion); tracker.load(); }; createScript('unity52/UnityConfig.js?v=' + consts.appVersion); }
Unity 5.6:
unityHelper.url = consts.isDebug ? "Debug/Build/Debug.json" : "Build/WebGL.json"; Module = UnityLoader.instantiate(gameContainer, unityHelper.url, { onProgress: function(a, b) { preloader.setProgress(b); }, Module: { resolveBuildUrl: function(buildUrl) { return (buildUrl.match("/(http|https|ftp|file):\/\//") ? buildUrl :unityHelper.url.substring(0, unityHelper.url.lastIndexOf("/") + 1) + buildUrl) + "?v=" + consts.appVersion; }, postRun: [unityHelper.postRun], }, }).Module;
New experience for new projects
On the implementation of the desktop version of Vikings: War of Clans took six months. Most of the time, we created a new UI and spent about two months doing technical work to migrate to WebGL.
Taken as Throne: Kingdom at War, we already knew what was to be done and in what time frame we could meet. Due to this, the second project moved to the web platform faster - in just 4 months. Note that in both cases, mobile games expanded their functionality, while their desktop versions were approaching the exit to production, so individual elements of the UI were adjusted already in the process.

Developer Tips
- Separate the logic and representation in the architecture of your application, and also try to highlight a single code base for the project. This will help avoid code duplication problems and make it easier to support all the platforms you use.
- In Unity WebGL, working with memory is different from other platforms. Initially, you need to specify WebGLMemorySize in player settings (default is 256 MB). The size of the allocated memory must be known in advance so that the browser can allocate space for your application. After that, you can no longer change the buffer size. Often, the content consumes a large amount of memory, and to determine its optimal size, you will have to test the application. This is best done with the Unity Profiler.
- When developing a WebGL project, you will inevitably encounter templates. Unity by default provides a simple HTML page with a progress bar to load your application, and also gives you the opportunity to implement your custom templates. If your application uses an internal preloader (to load resources, connect to the server, etc.), then to avoid alternately displaying two preloaders, it is better to limit to one that is available in the template. After loading your application through the Unity Loader, you should send events (events) about the loading process to the JS methods of the template, while hiding the template layer with the application for a while. This way you will avoid duplicating the preloader textures.
We hope that the experience of the studio Plarium-South will open up new opportunities for your projects.