My niece recently wrote to me asking me how computer games were created. Given that I am a web developer and not a games developer, I was at something of a loss to answer this, so I thought that I would try my hand at building a computer game so that I could know something about them and be able to give a coherent answer.
This article describes my efforts in developing this game and the challenges that I encountered. I'm reasonably pleased with the finished product although there are definitely some improvements that I'd like to make, some of which I will describe later on. I deliberately didn't use any of the frameworks that are available for building browser based games as I wanted to overcome all the challenges on my own. Whilst this is certainly more personally satisfying, it does mean that there are probably much better solutions out there, so if you are looking at the code for ideas, bear that in mind.
Moving Buzzy around
At first I wanted the user to move Buzzy around by simply clicking and dragging him with the mouse. This worked fine as long as the entire scene was contained within the viewport, but not when I introduced a feature called side scrolling. Side scrolling games are where the scene scrolls to the left as the player progresses through the game. The character may in fact even remain static in the same place on the screen whilst the background scrolls by him, creating the illusion of forward movement. This is a common technique, particularly in older 2D games such as Sonic the Hedgehog. Dragging with the mouse does not work because it requires the thing being dragged to actually move! If the dragged element remains in the same place, the cursor will continue to move until it reaches the edge of the screen - clearly not a good user experience! So instead of getting user input from the mouse, I got it from the keyboard instead, namely the arrow keys. This decouples the code that receives user input from that which handles moving Buzzy around and gives us the flexibility to create the effect of movement whichever way that we want.
However, I discovered that this create a new problem. In order for an animation to run smoothly, we need to update it every time a frame runs, that is, 60 times a second. We normally would do this in a requestAnimationFrame callback. However, previously the animation code was simply in the mousemove event handler. This seemed to work well and give us smooth animation because it turns out that the mousemove event handler actually runs about a 100 times a second. I found this rather surprising, to be honest, since it's faster than requestAnimationFrame runs! By contrast, the keypress events fire, when a key is held down, only about ten times a second. This results in extremely juddery animation.
The solution is to decouple the user input from the moving of the element itself. When the user presses a key, e.g. the right key, a boolean flag is set to true. When the user releases the key, the boolean flag is reset to false. The requestAnimationFrame callback checks the flag for each arrow key and if it finds it to be true moves the element by some distance in the appropriate direction. This gives us 60 frame per second animations with satisfying buttery smoothness.
I'd like to know exactly why the mousemove event fires so frequently. This does raise the question if we do want to run animation code in response to the mouse moving whether we should put it in the event handler or in the requestAnimationFrame callback. This needs a bit of research.
Being able to know when the objects within the game are in contact with one another is a crucial part of almost all computer games. I implemented a simple algorithm which detected when two rectangles had any coordinate in common. Collisions occur when Buzzy bumps into a wall or a bird, or at the end when he reaches the hive. At first, the collision zone for each object was just its bounding box, but I found that this resulted in a collision zone that was too large and collisions occurred when objects appeared quite distant from each other. Therefore, I gave each object a collision zone that was considerably smaller than its bounding box. Thus Buzzy can appear to touch other objects without necessary colliding with them, resulting in more pleasant game play.
As previously mentioned, for an animation to run smoothly it must run at around 60 frames per second. Anything else and it starts to become stuttery and jerky. I do find this cropping up every now and again. I have noticed also frames being dropped when doing a performance analysis using the Chrome dev tools. On my Macbook Pro, the game generally runs quite well but I will be interested to try it out on other machines, as well as receiving feedback from others who try it.
I have endeavoured as much as possible to follow performance best practices in order to reduce these problems. For example, all animation is carried out using translations rather than absolute positioning. This means that animated elements live on their own layer and are moved around by the compositor on the GPU rather than as part of the layout process on the main thread.
One issue that arose was a delay in images appearing when they were used for the first time. Buzzy has several visual states corresponding to what direction he is facing and what he's doing at the time, and there is a separate image for each of these states. The way CSS works is that the image is only loaded when it is used, thus whenever that first state is used there can be delay as the browser fetches the image from the server. The solution is to load all the images up front and we do this by displaying them in hidden elements that are positioned -9999px off screen.
Because this is a web based game, we need to consider how the game will look with different viewport sizes. If we used absolute units to define the dimensions of things, they would be relatively smaller in larger viewports, looking odd and also making the game easier. This is obviously unsatisfactory. To make objects in the game relatively sized, I have calculated ratios, when the game begins, for horizontal and vertical dimensions based on the size of the current viewport and used these to determine the actual dimensions. This isn't a perfect solution since odd behaviour can still occur if the ratio of the height to the width of the viewport is particularly unusual. I'll be interested to experiment here and also learn if anyone playing the game has experienced a viewport size that was particularly problematic.
Well obviously I learnt how to make a simple computer game! It's definitely a valuable thing to do for programmers and I would certainly recommend others try it. One thing in particular I think it forced me to do was to really think about performance.In a mainly static webpage, it's easy to overlook performance issues beyond page loading speed. Being able to record a performance timeline in Chrome Developer Tools and then analyse it is a skill that every developer should have.
I've pretty much achieved what I wanted to in this game, but I will reflect on this experience and I may want to do more work on it in the future. Things I might do are: add extra levels to the game; enable the recording of high scores; develop a way of measuring the score by distance travelled (less distance being better); and perhaps even develop entirely new games. I would also like to dig deeper into performance optimisation and see if I can reach that magic 60 frames per second constantly.