Games: Evolving and Interactive Images
Most games are quite unique compared to other types of visual software. Images, and videos made by software are never changed after they are created. Whatever visual or auditory representation exists in say a video, will exist every time it is watched. Games, on the other hand, always display different behavior that is dependent on whatever interaction the a player has with a game. Instead of representing graphics in very stable, reusable formats, like
.mp4 , games draw graphics onto designated screen objects, like a canvas, very frequently. This frequency is also called the frames per second or frame rate. The goal of making any game at a high level is to create an internal state of elements and objects that the player can see and control.
To begin, let’s write up the backbone of an HTML page with just a canvas element in it. This game in particular will be about ducks swimming in a pond, so for the background, a light blue is used to represent water.
This file will serve as the for games code. Thus far, if you open this
Events are Objects
When an event listener “hears” an event, it passes the event object to a call back function. That call back can then execute whatever commands that are desired to change the state of the game. To abstract this player input from events in an efficient manner , the call back function we register as an event listener will change an object called a controller. This controller will track which, if any, of the four directional arrow keys are currently pressed down. Here’s our controller:
This controller object is registered with both the
"keyup" events. The reason for this is later on, the goal is to check this controller to move the duck in the game. If there’s no key currently pressed down, the duck should stop moving. Otherwise, if one of
up , or
down is pressed, the duck should move in that direction. This controller is registered with the browser window with the following two calls:
Thus, the next step is to now implement our 🦆 friend, the duck.
Duck: The Game Element
The game implemented here allows moving a duck around a body of water. We can now check and read the input of a player using the arrows keys, but need to apply that input to the duck. The question is, how does a duck get drawn on the canvas ? In theory, it could be done through vector graphic commands, such as a path, arc, or series of lines. But that isn’t efficient for games. The internal objects of a game must be drawn at a very constant frequency, executing a bunch of stroke and fill commands on a canvas has a performance cost and limits the customization that can be achieved for characters. The best way to accomplish this for a 2D, top down game like this are sprites.
Sprites are visual representations of characters or elements with a finite range of positions. In 2D graphics, they are bitmaps that exist as part of the larger canvas. Typically, sprites have multiple image states to represent each direction of movement. Since we want our duck to be able to move in four directions, at a minimum, we will need four images, each one depicting the duck moving in a specific direction. To do this, I used a pixel art editor called pixilart . You can use any image editor or painting program for this though.
To make the duck small and more like a baby duck, I set him to be only 16x16 pixels. Here’s the four images :
The destroy step isn’t used in this setup. But if it were, you would want a destroy function to be called if an object in the game should be removed and no longer appear on the canvas. This might be the case in fighting games where the player defats an NPC. Lastly, the draw step involves taking the state of the object and translating that state onto the canvas. For our duck, that just involves using the canvas context
.drawImage(); method to put the sprite image on the canvas.
The above code represents the
Image() objects used to contain the sprite images as well as the duck object, possessing an action, destroy, and draw method. Here, each movement statement such as
this.y-- is contained within an
if block. Each of these
if statements check that the movement control coming from the player does not take the duck off the canvas perimeter. We don’t want our duck falling out of the water!
Putting it Altogether
Up to this point, we have went over the concept of events, their listeners, sprites, game objects, and drawing. The last, and final step, is to tie these elements together into a single graphic function that is called at certain frequency to readily paint the canvas. To do that, the overall state of our game, all of the objects, need to be sequestered into one single map, of the object name to the object body. Even though this particular game only has one object, it’s a good idea to understand code that can be used for more complex games with multiple objects.
The goal is that, the same protocol of actioning and drawing (with possible destruction) should be applied as if it were a polymorphic class or an interface across all game objects. We can encapsulate that logic into a function called
drawGame like so:
Every time the
drawGame function is called, we want to paint the canvas the background color to effectively wash away it’s previous state from the previous call. You may notice at the end of this snippet, there’s a call to the
n milliseconds. 10 milliseconds in this case means 100 frames per second. You can adjust the frame rate to see the duck in the game move slower or faster.
Now, the moment you have been waiting for. Here’s a few screenshots from the finished product
Overall, the HTML5 canvas is a powerful graphics tool that can be used to create fun and exciting games. One of the best part is it requires almost no setup or installation, just a browser and a text editor! If you want the complete game and all the code we worked on, that’s below:
If you want to take this game or a similar one to the next level, consider the following improvement ideas:
- Making an NPC that is controlled through an AI algorithm.
- Add some sort of object the duck should collect, and a points tracker.