Making a 3D web runner game #Bonus 3: Some UI refactors & improvements

In this final bonus episode, let’s rework and improve our UI a bit!

This article is also available on Medium.

Last time, we saw how to add some debug stats on our screen to help the devs: this was a brief introduction to tooling and the idea that you should think of your team as much as you do of you players!

This also means that you should have a clean codebase that is well-readable, well-organised and, if possible, pretty modular. This way, you can re-use code from one project to another and you gradually develop an internal ecosystem that gives your devs a safe and reassuring working environment.

For example, in our case, we have made our best efforts to extract the JS logic in its own file, in the Game class. That allowed us to have a pretty agnostic framework that simply encapsulates and calls these inner mechanics without knowing anything about how they actually work.

However, there is still one thing that the HTML index contains although it is very project-specific: the UI on the screen!

This means that, if we were to create another game based on the same mould, we’d have to modify this index to create the proper divs, and then reference them back in the Game class. This leaves a lot of room for errors… so, instead, it would be better to centralise everything in the JS class and have the Game class handle both the creation and the update of the UI.


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

HTML dynamic generation

The idea here is to generate our HTML elements dynamically: instead of writing them by hand in our HTML index, we are going to create our divs and other HTML elements using JavaScript built-in functions, and then insert them in the DOM tree.

We saw a couple of tutorials ago that we can use the document.createElement() built-in method to create new DOM items, and then the appendChild() method to add them into the tree.

What we’re going to do is create a new function in our Game class, _buildUI(), that is called from the constructor and that prepares all the game-specific HTML elements. It will also automatically assign the DOM references – instead of getting pre-existing divs by ID, we’ll simply store them upon creation:

Recreating our UI with JavaScript

Ok so – this is the structure that we need to reproduce: everything that is in our <body> tag:

Because it’s a tree, with a recursive nested structure, the idea is to first create the outer objects, so the ones that are at the root and have no parent item, add them to the body, and then create the sub-elements to add them to a newly created DOM item and recreate this hierarchy.

Generating our info panel dynamically

Let’s start with the info panel to see how it works.

First, I’ll create the outer div, the panel itself. Note that, even though I don’t need an ID to retrieve it in the JS, this ID is still used by my CSS, so I do need to set it for the new element. To set this ID, I can either use the setAttribute() method, or simply the id property, like this:

Finally, I just need to append it to the document.body HTML node:

To populate it, it’s pretty much the same: I just create a few elements of “div” type, set their ID or their class and append them, this time to my dynamically generated HTML info panel. I can fill in the contents of the divs with innerText or innerHTML:

For the last item, since we want an input and not a div, we have to be careful to use the “input” tag in our document.createElement() call. Then, we can use the setAttribute() method to specify the type, the min value, the max value and the disabled flag.

Note: by the way, “append” always means “add in last position” so the order of the nodes in the DOM tree will depend on the order of your function calls in the JS 😉

If you comment out the info panel in your HTML file and reload your page, you’ll see that we still have our info panel – but this time, it’s been entirely created in JavaScript! The references have automatically been assigned, too, so the data is properly updated as we play through a session.

Recreating the intro & game over panels

The intro and game over panels can be created with the exact same technique. They’re actually a bit simpler, even if they require two nested levels (first, the panel itself; then, the inner column div), because they only contain a few divs. So the process is similar to our previous DOM generation.

Adding some fade effects on the panels

Before we end this series, let’s work on a last UI improvement: making the panels fade instead of disappearing abruptly, like they do at the moment:

The idea is basically to have an additional class on those panels, the “hidden” class, that is toggled on or off when we want to show or hide the panel. Then, we’ll add some style so that a hidden panel has an opacity of 0, and a displayed panel has an opacity of 1. We’ll also add a UI transition to make the opacity gradually lerp between those values over the course of 1.5s if we enable or disable the “hidden” class.

Note: for more info on UI transitions and animations, you can check out another article I posted recently about a few CSS tricks 😉

I will also make sure that, when they’re disabled, the panels don’t catch the user interactions:

And while we’re at it, we can also remove the specific style we had previously on the game over panel to hide it at first, since we are now going to use our new “hidden” class to handle this; so just remove the #game-over { ... } style in your CSS file 🙂

To wrap this up, let’s go back to the Game class and, when we click on our start or replay buttons, we’ll want to replace our style modification with a class switch. To do this, we can simply use the classList property and then add the “hidden” class:

For the game over panel, we’ll also need to initially hide it, by setting the class name to also contain “hidden” upon creation, and then remove it when we run our game over logic.

Conclusion

Alright!

Today, we’ve reworked our UI system to transfer everything to the Game class and we now have a very basic but very agnostic game framework that could easily be re-used to create other web-based games. Everything is centralised in our JavaScript Game class and the codebase is well organised to make it easier to start or maintain projects.

This episode also marks the end of this series of tutorials on how to make a web 3D infinite runner game. I really hope you enjoyed it and that you learnt a few things – don’t hesitate to react in the comment, or to like and share the video and the article!

Thanks a lot for following me during this adventure, and stay tuned for new articles on tech, video games and coding 🙂

Leave a Reply

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