Time to get started on the core components of the game engine. We should be able to render something on the Canvas before the end of this article!
Note: please be aware that I’ll add links to specific pieces of code in the GIT repository history tree. This will make it easier for you to run the same code I did when describing a specific feature.
We’ll start by working on the classes that have no dependencies like: Game, AssetLoader and EventDispatcher then progress onto others that might extend these.
The Game class could:
There are some improvements to be made here like checking if the added scenes are actual Scene instances, adding support for browsers which don’t have requestAnimationFrame, maybe implement a flux architecture to store data and prevent state mutation.
The flux architecture might come at a later time but for now we’re treating the engine as a P.O.C. (proof of concept)… that implies that I also won’t bother adding support for older browsers, but you can definitely use the rAF polyfill if you need it.
The AssetLoader class could:
You would generally implement a getInstance() method on a singleton class, here we’re simply returning the previously created instance whenever we’re calling the class constructor.
We also have a dependency here on a Utils class, it currently just handles the asset “name to camel case” logic; it looks like this:
This one is actually a static class, and to enforce that we’re simply throwing an error in its constructor. That means trying to call new Utils() will fail. The class is fairly simple for now as it just has a single static method; we’ll probably add more later.
The EventDispatcher class could:
You might think this one looks weirder than the rest, and there’s a good reason for that. It’s a bit tricky to extend a singleton using the ES6 syntax, read my article describing the issue here.
We’re not done yet; we still have a bit of coding to do before we can draw anything. According to our schema in the previous article we can now focus on implementing the Keyboard class which extends EventDispatcher.
The Keyboard class could:
It should be sufficient to expose getters for the keys A to Z, space, arrow keys, tab, enter, shift, ctrl, alt, esc and functional keys F1 to F12.
This class also uses KeyboardEvents, which looks like this:
This class holds some static methods which return event names that basically serve as constants. You might be wondering why I didn’t simply do:
export const KEY_UP = 'keyup';
It’s because this approach allows for the import of the class and through that class we gain access to all the Keyboard related static getters. We don’t need to import each and every constant separately.
Since we finally have the Keyboard class, we can focus on the Canvas next.
The Canvas class could:
At this point we could draw on the Canvas directly by using the ctx property but that’s boring and silly because it wouldn’t be using any of the logic we worked so had to build in the other classes. Let’s focus on building one more class which will help us out, namely: Scene.
The Scene class could:
We have a Scene and based on its logic we can add an element to it, which will be updated and rendered… provided that the element has the update() and render() methods.
And now for the moment we’ve all been waiting for; this is how you draw a couple of moving squares the super complicated way…
Create a new folder in the js directory (on the same level as the engine directory) and name it square. Create an index.js file with this logic:
For the sake of brevity I created the Square class in the same file but we now have a basic engine for creating and rendering static or moving objects on the canvas.
The squares move on the x and y axis at a predefined speed and if you press SHIFT, that speed is doubled. The objects’ position is wrapped to the parent Screen so you can play around with it. Don’t forget that you’re inheriting all the Keyboard functionality there, so have fun with that.
Grab the files and test it out here:
Obviously there are improvements to be made, like: using “const” instead of “let” for all singleton instances, figuring out how to use private variables in classes that have multiple instances without causing overwrites (and if you’re thinking “WeakMap”… that will probably do more harm than good) and using a store for our game engine; just to mention a few.
We’ll continue with a discussion on how movement works in games, cover sprite sheets, sprites, animated sprites, controlling frame rate, display object pivot points and another implementation example in the next article.
Create your free account to unlock your custom reading experience.