Making a 3D web runner game #Bonus 2: Adding stats panels

In this second bonus episode, let’s do some tooling to help the devs debug 🙂

This article is also available on Medium.

In the first bonus episode, we added various improvements to our game like camera shakes, fog in the distance or bonus popup labels. These are little things that make the overall player experience better.

But it’s also important to think of the people making your game and that’s why tooling is important!


This tutorial is available either in video format or in text format — see below 🙂

A quick recap on tooling

In a nutshell, tooling is a specific subdomain of development that specially focuses on making interfaces and consoles to monitor internal and debug info on your program, and give some feedback to the devs on the project.

So it’s about creating tools that aren’t always pretty, but that’s not the point. The point is to show data in an intuitively readable manner so that the people who work on the game can analyse it easily and potentially find or fix bugs.

The tools can take various forms, from just an in-game console to a full-fledged dynamic inspector of a 3D scene hierarchy. They have to be made specifically for your team and adapted to your needs, they are not generic. Finally, depending on the tool, it may or may not be exported in the final shipped game build.

In our case, we are going to create a basic stats panel that shows the current frame render time, allocated memory and FPS of the app and that can be toggled by pressing the <Enter> key. We don’t really discriminate between dev and prod builds, so we can say that it will be shipped in the final version – even if not many players will take a look at it! 😉

Installing stats.js

To create this debug panel, we will use another small JavaScript library called stats.js.

This lib is a lightweight class that allows you to quickly spawn info graphs in your DOM that give you a real-time analysis of predefined or custom user-computed values. In our case, we’ll mostly stick with the predefined ones and focus on the FPS, MS and MB stats.

As explained in the docs:

  • FPS is the number of frames that were rendered in the last second.
  • MS is the number of milliseconds needed to render a frame.
  • MB is the amount of memory allocated to our app.

As usual, we can get a minified version of the lib by going on the Github, in the build/ folder; and copy this .min.js file to our vendors/ folder.

Then, we’ll simply import it in the index.html and we’re ready to go!

Instantiating our stats panels

To keep our codebase clean, let’s create a new file called stats-manager.js and import it in the index.html as well.

Then, inside this file, let’s create a small class called StatsManager:

This class will first create our stats panels and add them to the DOM, and then store a reference to each of these panels so that it can update them later on in our animate loop.

But before we actually dive into the JS, let’s first create a container for this info in our HTML file. We’ll just create a new div with id “stats” in our <body> tag:

Then, in our CSS file, we can add some style to this div so that we’re sure it is printed on top of the rest of our elements, in the top-right corner.

And finally, we can now use the stats.js lib to actually populate this div with debug panels!

Let’s go back to our stats-manager.js file and, in the StatsManager class, add a constructor.

In here, we’ll first get a reference to our stats div. Once again, I use the document.getElementById() built-in method. Then, I’ll prepare an array to store my stat panels. These are the JavaScript objects that I will use and update in the animate() function afterwards.

And so these objects are created by using the stats.js lib, just by creating a new Stats() object and calling its showPanel() method with the right debug panel id. For example, id 0 is the FPS panel, then id 1 is the MS variable and finally id 2 gives me the allocated memory stat.

Since I want those three panels, I can wrap my panel creation inside a basic for-loop that goes from 0 to 2 (included) and calls the JS constructor and its showPanel() method. Once I’ve created my panel, I need to show it in the DOM by appending its DOM element to my stats div.

And, finally, I’ll push this new stat panel to my list of stats.

With this little loop, I therefore have my 3 panels properly set up.

If I go to my main.js and create a new StatsManager in my onload() callback, you see that when I refresh my page I now have a bunch of debug panels on the right!

Something important to note, though, is that the memory debug relies on the memory property of the Performance object which is an experimental feature that is only available for chromium-based browsers, but for example not on Firefox or Safari.

So if you run the code on a Firefox browser, you won’t see the third panel…

Actually updating the data

With that said, you also see that, at the moment, those panels aren’t really displaying any data. They have been added to the DOM as empty frames and stay still all throughout the game.

To have them update and show the up-to-date data, we need to call the update() method of the Stats objects we stored.

Let’s do this in a new function, inside our StatsManager, called update(). This method will just iterate through our this.stats array and call the update() function on each item:

Then, in our main.js file, in the animate loop, just below the gameInstance.update() line, we can add to update the stats manager, too:

And if we refresh this, we see that the panels are now updating and showing some actual data 🙂

As you run the game, you should see some slight fluctuations – but our game is clearly not too heavy, so these should stay at pretty good levels on most computers!

Adding a toggle shortcut

Finally, let’s add a shortcut to toggle our debug panels on and off – this way, we’ll initially hide it and only the ones who are interested will display it.

In our StatsManager class, we’ll add a new boolean flag, this.active, that will prevent us from updating the data if the panel is hidden. It’s like the running flag in our Game instance: if this value is false, then we’ll skip the update process altogether.

This will avoid doing useless computation and improve the performance of our app a little bit.

Then, let’s also add a _toggle() function that switches the value of this flag and shows or hides the this.statsDiv panel accordingly:

And to wrap this up, let’s do the same as in our Game class and have a callback on the keydown event that calls this toggle function if we press the <Enter> key:

I’ll also call the _toggle() function once at the end of the constructor so that the panel is hidden at first.

And now, whenever I press the Enter key, my panel is toggled on or off and the data is updated only when I see the stats panels.

Conclusion

So there you have it! 🙂

In this episode, we saw how to add simple debug panels that can be toggled on or off using the stats.js library. It is not fully cross-browser compatible because it relies on the memory property of the Performance API that is not available everywhere, but it is already a basic example of tooling, and it can help you quickly catch if something is wrong with your game.

Next time, the third bonus episode will focus on UI and HTML manipulation via JavaScript: we’ll see how to replace some of our HTML hand-written elements with JS dynamically generated ones, and see how this makes the index even more agnostic of the game than it is at the moment.

Leave a Reply

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