{"id":131,"date":"2015-05-25T16:42:55","date_gmt":"2015-05-25T06:42:55","guid":{"rendered":"https:\/\/ocias.com\/blog\/?p=131"},"modified":"2019-06-13T22:49:02","modified_gmt":"2019-06-13T12:49:02","slug":"unity-webgl-custom-progress-bar","status":"publish","type":"post","link":"https:\/\/ocias.com\/blog\/unity-webgl-custom-progress-bar\/","title":{"rendered":"Unity WebGL Custom Progress Bar"},"content":{"rendered":"<p>When making a browser-based game, you&#8217;re asking players to be generous with their time and wait patiently while your content downloads. This means how you indicate download progress in your preloader is super important. A Unity webGL custom progress bar is essential.<\/p>\n<p><!--more--><\/p>\n<p>The default Unity webGL progress bar is a awful.<\/p>\n<ul>\n<li>It doesn&#8217;t update live, instead jumping in big chunks.<\/li>\n<li>Doesn&#8217;t give any idea as to how large the download is.<\/li>\n<li>When the download completes it just hangs for up to two minutes without any indication as to what&#8217;s going on!<\/li>\n<\/ul>\n<p>A terrible user experience, meaning most potential players will drop off before your game even loads. Let&#8217;s look at how we can improve this.<\/p>\n<p><a href=\"https:\/\/ocias.com\/games\/the-yearning-tree\/\">Here&#8217;s an example of a Unity webGL game with a preloader that fixes these issues<\/a>. This is what we&#8217;re going to make.<\/p>\n<p>It&#8217;s best to start by basing a new template off the default one (<a href=\"https:\/\/ocias.com\/blog\/how-to-set-up-a-unity-webgl-template\/\">I wrote a guide on how to set up templates here<\/a>).<\/p>\n<p>By default, the code that affects how our preloader works is located in TemplateData\/unityProgress.js. Looking through this file you&#8217;ll see that it&#8217;s adding all the html elements to display the progress bar\u00a0programmatically. This is a bit difficult to work with\u00a0(and not really the way Javascript is intended to be used), so the first thing to do is move all the html into our index.html file instead.<\/p>\n<h2>Setting Up a Basic Unity WebGL Custom Progress Bar<\/h2>\n<p>Strip all the display code from unity progress.js until you&#8217;re left with:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">function UnityProgress (dom) {\n  this.progress = 0.0;\n  this.message = \"\";\n  this.dom = dom;\n\n  var parent = dom.parentNode;\n\n  this.SetProgress = function (progress) { \n    if (this.progress &lt; progress)\n      this.progress = progress;\n\n    this.Update();\n  }\n\n  this.SetMessage = function (message) { \n    this.message = message; \n    this.Update();\n  }\n\n  this.Clear = function() {\n\n  }\n\n  this.Update = function() {\n\n  }\n\n  this.Update ();\n}<\/pre>\n<p>Next we make the\u00a0html + css progress display. The things I find pretty necessary are:<\/p>\n<ul>\n<li>A Loading Bar<\/li>\n<li>A Loading bar background<\/li>\n<li>Progress update text (&#8220;X\/Y downloaded&#8221; etc)<\/li>\n<\/ul>\n<p>All wrapped in its own div in index.html, just after the canvas:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">&lt;div id=\"loadingBox\"&gt;\n  &lt;div id=\"bgBar\"&gt;&lt;\/div&gt;\n  &lt;div id=\"progressBar\"&gt;&lt;\/div&gt;\n  &lt;p id=\"loadingInfo\"&gt;Loading...&lt;\/p&gt;\n&lt;\/div&gt;<\/pre>\n<p>Add some styling (inline or in the header or linked in a separate file).\u00a0I&#8217;ll go with a style that fits into\u00a0a <a href=\"https:\/\/ocias.com\/blog\/full-browser-window-unity-webgl\/\">full browser window template<\/a>. First we centre the whole box:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"css\">div#loadingBox {\n  width: 100%;\n  height: 20px;\n  position: absolute;\n  top: 50%;\n  margin-top: -10px;\n  text-align: center;\n}<\/pre>\n<p>Then sort out the sizing, colouring and positioning of the loading bars:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"css\">div#bgBar {\n  position: absolute;\n  width: 200px;\n  margin-left: -100px;\n  left: 50%;\n  height: 2px;\n  display: block;\n  background-color: #333;\n}\n\ndiv#progressBar {\n  left: 50%;\n  position: absolute;\n  margin-left: -100px;\n  width: 0px;\n  height: 2px;\n  background-color: white;\n  border-radius: 2px;\n}\n\ndiv#bgBar {\n  border-radius: 2px;\n}<\/pre>\n<p>And just style up the loading progress text a tiny bit too:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"css\">p#loadingInfo {\n  color: #666;\n  letter-spacing: 1px;\n  position: absolute;\n  width: 100%;\n  font-family: \"Monaco\", sans-serif;\n  text-transform: uppercase;\n  text-align: center;\n  font-size: 8px;\n  margin-top: 10px;\n}<\/pre>\n<p>Once the loading information displays the way you want it to, we just need to connect it up to the loading data in unity progress.js.<\/p>\n<p>Hide everything when loading is complete in SetProgress() and Clear(), and connect up the progress to your CSS and loading message in Update():<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-highlight=\"12-16,26,30-33\">function UnityProgress (dom) {\n  this.progress = 0.0;\n  this.message = \"\";\n  this.dom = dom;\n\n  var parent = dom.parentNode;\n  \n  this.SetProgress = function (progress) { \n    if (this.progress &lt; progress)\n      this.progress = progress; \n\n    if (progress == 1) {\n      this.SetMessage(\"Preparing...\");\n      document.getElementById(\"bgBar\").style.display = \"none\";\n      document.getElementById(\"progressBar\").style.display = \"none\";\n    } \n    this.Update();\n  }\n\n  this.SetMessage = function (message) { \n    this.message = message; \n    this.Update();\n  }\n\n  this.Clear = function() {\n    document.getElementById(\"loadingBox\").style.display = \"none\";\n  }\n\n  this.Update = function() {\n    var length = 200 * Math.min(this.progress, 1);\n    bar = document.getElementById(\"progressBar\")\n    bar.style.width = length + \"px\";\n    document.getElementById(\"loadingInfo\").innerHTML = this.message;\n  }\n\n  this.Update ();\n}<\/pre>\n<h2>Advanced Preloader Features<\/h2>\n<p>Now we&#8217;ve got our own tidy progress bar and update text,\u00a0but it still moves in big chunks instead of smoothly growing, and doesn&#8217;t feel like it&#8217;s making progress after the download completes. Let&#8217;s fix the jumps using <a href=\"http:\/\/www.createjs.com\/TweenJS\">TweenJS<\/a>, and we&#8217;ll display a spinner gif after the download reaches 100% to keep some motion on screen.<\/p>\n<p>Download the <a href=\"http:\/\/www.createjs.com\/TweenJS\">TweenJS<\/a> package and copy over the CSSPlugin.js, then attach it to your index.html:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">&lt;script type=\"text\/javascript\" src=\"https:\/\/code.createjs.com\/tweenjs-0.6.0.min.js\"&gt;&lt;\/script&gt;\n&lt;script type=\"text\/javascript\" src=\"TemplateData\/CSSPlugin.js\"&gt;&lt;\/script&gt;<\/pre>\n<p>Then make or <a href=\"http:\/\/www.ajaxload.info\">download a gif spinner<\/a>, and add it to your loader html:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">&lt;img id=\"spinner\" src=\"TemplateData\/spinner.gif\" style=\"display: none; margin: 0 auto\" \/&gt;<\/pre>\n<p>Now just connect these two parts:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-highlight=\"1-3,10-11,22,41-42\">function init() {\n    \n}\n\nfunction UnityProgress (dom) {\n  this.progress = 0.0;\n  this.message = \"\";\n  this.dom = dom;\n  \n  createjs.CSSPlugin.install(createjs.Tween);\n  createjs.Ticker.setFPS(60);\n  \n\n  var parent = dom.parentNode;\n  \n  this.SetProgress = function (progress) { \n    if (this.progress &lt; progress)\n      this.progress = progress; \n\n    if (progress == 1) {\n      this.SetMessage(\"Preparing...\");\n      document.getElementById(\"spinner\").style.display = \"inherit\";\n      document.getElementById(\"bgBar\").style.display = \"none\";\n      document.getElementById(\"progressBar\").style.display = \"none\";\n    } \n    this.Update();\n  }\n\n  this.SetMessage = function (message) { \n    this.message = message; \n    this.Update();\n  }\n\n  this.Clear = function() {\n    document.getElementById(\"loadingBox\").style.display = \"none\";\n  }\n\n  this.Update = function() {\n    var length = 200 * Math.min(this.progress, 1);\n    bar = document.getElementById(\"progressBar\")\n    createjs.Tween.removeTweens(bar);\n    createjs.Tween.get(bar).to({width: length}, 500, createjs.Ease.sineOut);\n    document.getElementById(\"loadingInfo\").innerHTML = this.message;\n  }\n\n  this.Update ();\n}<\/pre>\n<p>Done! Now you have an elegant preloader with much nicer UX that keeps your player up to date with exactly how close they are to playing the game.<\/p>\n<h2>Tips and Pitfalls<\/h2>\n<ul>\n<li>For your loading text, use a monospaced font. As progress grows, the visual size of the load should grow. Using a non-monospaced font will cause the visual size to jitter instead.<\/li>\n<\/ul>\n<p>Run into any problems? Need my help on making some lovely UX \u00a0in browser games? <a href=\"https:\/\/ocias.com\/contactme.php\">Get in touch<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When making a browser-based game, you&#8217;re asking players to be generous with their time and wait patiently while your content downloads. This means how you indicate download progress in your preloader is super important. A Unity webGL custom progress bar is essential.<\/p>\n","protected":false},"author":1,"featured_media":147,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[1],"tags":[9,3,10],"class_list":["post-131","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorised","tag-tutorial","tag-unity3d","tag-webgl"],"_links":{"self":[{"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/posts\/131","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/comments?post=131"}],"version-history":[{"count":10,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/posts\/131\/revisions"}],"predecessor-version":[{"id":446,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/posts\/131\/revisions\/446"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/media\/147"}],"wp:attachment":[{"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/media?parent=131"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/categories?post=131"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/tags?post=131"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}