JavaScript and HTML5 – Simple Game Creation Tutorial (part 5)

I know I promised goodness gracious great balls of fire in part 5…but I changed my mind. I talked with a couple friends and they pointed out that it was a little annoying that the game was a fixed size and just sat there at 800×600 no matter how big they made the screen size / browser window. To remedy that, this post is going to be step through the process of implementing scaling into our context rendering. Fair warning, I have never done this with the HTML5 / JavaScript objects and haven’t even done it with DirectX for years now. This may not be the best way to do it! That having been said, this method seems to work for me in all the major desktop browsers I tried (Safari, Opera, IE 9, Firefox, Chrome) but if you know of a better way to do it, please let me know.

I’m going to assume that if you’re reading part 5 of the series you’ve already at least looked through part 1, part 2, part 3 and part 4. If you haven’t, I highly recommend you at least familiarize yourself with them somewhat or you may get lost. Then again, you may already know how to do this and just came to read my blog and mock me for the stupid decisions I make as I’m learning this stuff…I just made myself sad.

Back on topic now. The concept of scaling isn’t all that complicated in reality. What we want to do is tell the game that we want the resolution to be 800×600 but that we want that to stretch (or shrink) to fill all of the screen real estate the browser has available. Previously we had created two variables (gameW and gameH) that held the dimensions of our game and everything was driven off of those. In order to get our scaling working properly we are going to expand from those two variables up to eight different variables:

As you can see by the comments (go go comments) the gameW and gameH variables are now going to be holding the “true” dimensions of the game window (browser). The baseW and baseH variables are our static variables that tell the game what we want the resolution to be (feel free to change those around and see how things change later). Variables renderW and renderH are driven off of a comparison between the gameW/gameH and the baseW/baseH and will be used in our actual render methods for the objects being drawn to the screen. The scaleX and scaleY variables do just what you’d expect, they hold the scale ratio between what we want to show and what screen size we actual have.

There are a couple different ways I tried for doing this:

  1. Keeping it always at the base resolution and centering it in the browser — I didn’t go this route because it wasn’t really any improvement over doing nothing at all
  2. Maintaining a static aspect ratio and scaling to fill the window — this looks nicer, in my opinion, because the rendered objects are never stretched or squashed, but has the draw back of things not fitting properly if the window is resized to a different aspect ratio than at load time
  3. Not maintaining the aspect ratio and scaling to fill the window — this can cause objects to be squashed or stretched if the browser window is not of a size that keeps the 800×600 ratio, but things always fit properly on the screen

In the demo we’ll be implementing both 2 and 3 and providing a checkbox so you can toggle between the two methods. I’m not 100% sure which I like best of all, so I’ll wait and see if I get any feedback about which one other people like most and go forward with that one when we add the fireballs (I’m going to do my best to get that in part 6!).

As I mentioned at the start, we’ll be using the renderW and renderH variables for rendering out our objects. Because of this there are a couple changes we need to make. In each of our object methods we need to update the this.x and this.y assignments to use renderW/renderH instead of baseW/baseH. For the staticObject() we only need to change the assignment lines since it has no update method:

In the heroObject() we’ll change the x/y assignments:

We also need to change the code in the update() method for the heroObject that handled wrapping around the screen:

Another change is required to our initRocks() function because we had the logic in place that calculated a new x/y position for each rock when there was a collision:

And the final change is inside the gameLoop to our clearRect call

The next bit of code we’ll put in will be a new function that will be used to calculate the values for the new variables we added at the top. I set this up to accept a parameter that determines whether or not we want to maintain the aspect ratio or not. If we want to maintain the aspect ratio then we take the following steps: determine what our current width-to-height ratio is, set our gameW and gameH, set our renderW and renderH using a ratio calculation to keep it constant, set our scaleX and scaleY to the same value since the ratio is being maintained. If we don’t care about maintaining the aspect ratio then our steps are simplified (and have no “real” logic): set our gameW and gameH, set our renderW and renderH to the baseW and baseH values, calculate our scaleX and scaleY values. The scaling function will look like this:

We’re going to need to adjust

With that out of the way we’ll dive into the

At this point you should be able to run the game and see it fill the entire screen, stretching / squashing the objects as needed to do so. You’ll most likely notice that resizing the window has no effect. That’s easy enough to fix by hooking to the window resize event, but there are a couple things that could end up biting us in the patootie if we don’t watch out. We don’t want the resize event to reinitialize everything, because then our hero, enemies and rocks will all be regenerated and positioned differently on the screen. We’ll create a new function to handle resizing that will do the following: recalculate the scaling, reset our canvas width and height, clear our the context objects, restore to the saved context information, re-apply our scaling and re-render all of the objects. We will not reinitialize anything entirely because we want to keep the same objects we had prior to resizing:

Initially, I just wired the call to this function directly into the window resize event…that ended up not being the best idea. Firefox and Chrome (maybe others) trigger the resize event constantly while the window is resizing and cause a lot of extra calculations to be done if we call the resizeHandler directly from the event. Instead I created a simple customTimeout function that we can use to simulate a “resizeComplete” event. The customTimeout will make use of a timer object and the base setTimeout function to accomplish our goals:

Now to wire that up to the resize event we use a single line of jQuery

Voila, we have a “fullscreen” game that will adjust and resize the objects as the browser window is resized. Pressing F11 and going into fullscreen mode with your browser should cause the game to truly be fullscreen, but any resize should work as well (even making it really tiny).

Hopefully I managed to have this all make sense and didn’t do anything incredibly stupid. If I did, please don’t hesitate to let me know and I’ll update the post accordingly. Here’s what we should have at this point (I resized the window down to a fairly small size):

Simple Game - Sample Image 5

Here’s the link to the demo for this code.