Christopher Bennage

not all who wander are lost

Building a Game With JavaScript

See the introduction post for context.

The Loop

In general, game development begins with the game loop. If you come from the business world of software development, this will be strange. You don’t rely on events. Phil Haack once asked me “why a loop?”, to which I responded “uh…”. However, a much better answer would have been this one on stackoverflow.

Okay, so we should use a master loop. If our runtime is the browser, how do setup this loop? There’s a relatively new API called requestAnimationFrame and that’s what most folks recommend. Check out these for details:

(I do recall reading something negative along the way about the API, but I couldn’t find it again.)

I used the requestAnimationFrame shim referenced in the Paul Irish post above. The shim is only necessary for older browsers that have not implemented the API. By the way, we refer to each iteration of the loop as a “frame” because of the analogy with traditional animation.

Implementation

Now that we’ve ensured that requestAnimationFrame is present we can get to our game loop. Here is my game’s bootstrap code (well, an early version of it):

The heart of this the loop function. It has the following step:

  • capture the current time
  • calculate the time that has elasped since the last frame
  • execute the game’s logic for the frame (that’s the update and draw invocations)
  • request the next invocation of loop using requestAnimationFrame
  • record the current time of the this frame for calculations in the next one

N.B. This code doesn’t use frameId yet. The idea is that this handle could be used to halt the loop.

The beginLoop function is there merely to provide a closure for some of the variables used to track the state of the loop. It kicks off the loop with its initial invocation of loop.

The big mystery inside of loop is the currentScreen object. Here I was thinking ahead (which can be dangerous). I know that my game will have at least two “screens”, possibly more:

  • start menu screen
  • main game screen (where the action takes place)

I wanted the loop logic to work with both (as well as any future screens). I expect screen objects to have two methods:

  • update takes the time elapsed since the last frame and is responsible for updating the state of the game.
  • draw takes the drawing context (from the canvas) and is responsible for rendering the current state of the game.

You’ll also see that I grab a canvas element and capture its drawing context. If you are not familiar with the canvas APIs, I recommend that you start here.

Why two different methods for game logic?

Keeping the update and draw functions separate is important. When frames becomes expensive to compute, the game may respond with lag or non-deterministic behavior. Too avoid this, you might want your game to skip over some logic during a particular iteration of the loop. However, it’s very likely that you won’t want to drop calls to update. It’s not necessary a big deal if you skip rendering a couple of frames, however if skip calculating the location of a projectile then it might mysteriously “pass through” its target. This will become more relevant to us in particular, because I’d like to all the player to control the speed of game (a common feature of many tower defense games).

Right now update and draw are always called for each iteration of the loop, so the distinction is academic in this context. We could though calculate our frame rate in loop and occasionally skip invoking draw if the rate slowed down.

Now we have enough in place to begin working on our start menu screen.

next ยป

Comments