/* */

Lights Off – A puzzle game using HTML5 canvas

More and more HTML5 tutorials are popping up on the internet. Although it still is future talk, it’s still great to see what this upcoming web standard can do for us.

One of the most interesting new HTML5 elements, has to be <canvas>. This element allows the developer to draw anything using JavaScript. I’ve never played around with this element, but still wanted to experiment with it.

Therefor, I re-created Lights Off, one of the first native games for the iPhone, but now using HTML5 <canvas>.

I know my attempt to create the game isn’t nearly as great as the real app, but the goal for this tutorial is to see how the <canvas> element works. That said, this demo only works on browsers that support the element.

Can you solve the puzzle? Simply click on a light panel to toggle it along with the four adjacent lights. Or dive into the code and learn some more about the <canvas> element.

Video

Here’s a video of the game in action (shown in Firefox 3.6) for those who don’t have a browser that support the HTML5 <canvas> element.

Let’s see how you can create something like that yourself.

HTML/CSS

The HTML and CSS isn’t that very spectecular: Simply the <canvas> with a specified ID, and a container styled with some CSS (the green background).


<div id="lightcontainer">
   <canvas id="lightpanel" width="500" height="500"></canvas>
</div>


#lightcontainer { margin:20px auto; width:500px; }
#lightpanel { background-color:#002B33;
   -moz-box-shadow:0 0 5px #999; -moz-border-radius:10px;
   -webkit-box-shadow:0 0 5px #999; -webkit-border-radius:10px;
   box-shadow:0 0 5px #999; border-radius:10px;
}

Now on to the next, more interesting steps.

The idea

Here are a couple of images to explain the idea. I hope it makes things clear.

Step 1

First, we’ll need to create the playing field. This is a field with a size of 5×5 light panels.

Step 2

Next, we’ll need to apply logic for the light switch. As you can see in the example, it turns on or off the panel and the horizontal and diagonal surrounding panels. If the panel was on, it’ll be turned off; When it’s turned off, it’ll be turned on. The red arrows show where the user would have clicked in an empty field.

Step 3

Now to create the starting position and let the user try to get off all the lights to finish the game! Of course, after each click we need to check if all the lights are off and the player is done.

JavaScript

And here’s the JavaScript I created (I used a little bit of jQuery). I’m not going to dive really deep into the code, since comments are added which explain a lot.


// Two dimensional array that represents the playing field
// A "x" stands for "on"
// A "o" stands for "off"
var lightField =
[
   [ "x", "o", "o", "x", "x" ],
   [ "o", "o", "x", "o", "x" ],
   [ "o", "x", "o", "x", "o" ],
   [ "x", "o", "x", "o", "o" ],
   [ "x", "x", "o", "o", "x" ]
];

I think a two dimensional array is perfect to create a playing field that we want. It’s easy to work with (in code), but also great to see how the field is going to look like eventually, since the code exactly represents the field.


// Attach a mouse click event listener
$("#lightpanel").click(function(e) {
      
   // e will give us absolute x, y so we need to calculate relative to canvas position
   var pos = $("#lightpanel").position();
   var ox = e.pageX - pos.left;
   var oy = e.pageY - pos.top;
      
   // Check which fields we need to flip
   // 100 = width of the tile
   var yField = Math.floor(oy / 100);
   var xField = Math.floor(ox / 100);
      
   // The field itself
   lightField[yField][xField] = lightField[yField][xField] == "x" ? "o" : "x";
      
   // The field above
   if(yField-1 >= 0) {
      lightField[yField-1][xField] = lightField[yField-1][xField] == "x" ? "o" : "x";
   }
      
   // The field underneath
   if(yField+1 < 5) {
      lightField[yField+1][xField] = lightField[yField+1][xField] == "x" ? "o" : "x";
   }
      
   // The field to the left
   if(xField-1 >= 0) {
      lightField[yField][xField-1] = lightField[yField][xField-1] == "x" ? "o" : "x";   
   }
      
   // The field to the right
   if(xField+1 < 5) {
      lightField[yField][xField+1] = lightField[yField][xField+1] == "x" ? "o" : "x";   
   }
      
   repaintPanel();
});

This function attaches the mouse listener to the playing field and checks which field ths user clicked on. Based on that information, it flips the surrounding fields (using the single line IF statement). We check for >= 0 and < 5 since we don't want to get out the bounds of the array.

As you can see, this function calls the repaintPanel() at the bottom, which (ofcourse) repaints the panel. Now let's see how that function looks like.


function repaintPanel() {
      
   // Retrieve the canvas
   var canvas = document.getElementById("lightpanel");
      
   // Check if the browser supports <canvas>
   if (!canvas.getContext){
      alert("This demo requires a browser that supports the <canvas> element.");
      return;
   } else {
      clear();
         
      // Get the context to draw on
      var ctx = canvas.getContext("2d");
         
      // Create the fields
      var allLightsAreOff = true;
      for(var i = 0; i < lightField.length; i++) { // Rows
         for (var j = 0; j < lightField[i].length; j++) { // Columns
               
            // Set up the brush
            ctx.lineWidth = 3;
            ctx.strokeStyle = "#83BD08";
               
            // Start drawing
            ctx.beginPath();
                 
            // arc( x, y, radius, startAngle, endAngle, anticlockwise)
            ctx.arc(j * 100 + 50, i * 100 + 50, 40, 0, Math.PI*2, true);
                 
            // Actual draw of the border
            ctx.stroke();
                  
            // Check if we need to fill the border
            if(lightField[i][j] == "x") {
               ctx.fillStyle = "#FFBD38";
               ctx.beginPath();
               ctx.arc(j * 100 + 50, i * 100 + 50, 38, 0, Math.PI*2, true);
               ctx.fill();
                     
               // Since we need to fill this field, not all the lights are off
               allLightsAreOff = false;
             }   
         }
      }
         
      // Check if all the lights are off
      if(allLightsAreOff) {
         // User can't click anymore
         userCanClick = false;
            
         // Show message
         alert("All lights are off, you finished the game!");
      }
   }
}

I hope my comments made this part - the most interesting part of this tutorial - clear. We're simply retrieving the canvas and draw the field on it.

Since the element doesn't clear the drawing after we re-draw, we need to manually clear the contents. That's why we first call the clear() method.


function clear() {
   var canvas = document.getElementById("lightpanel");
   var ctx = canvas.getContext("2d");
   ctx.clearRect(0, 0, 500, 500);
}

That's all the code we need to create this neat little game!

Conclusion and Download

I know the design could be improved in loads of ways, but I just wanted to experiment with the <canvas> element. We could also introduce a high score and level system, but that was not the goal for this tutorial/example.

I hope you learned something and use the <canvas> element in one of your future projects. How was the ride? Could the code be improved? Feel free to share.

Leave a reply:

Your email address will not be published.

Site Footer