A Simple jQuery Plugin Game

Today we are making a simple puzzle game called “Fifteen”. The purpose of the game is to slide 15 square blocks around to form an image. The goal of this tutorial is to look at this simple browser-based game and explain how it was made line by line. It’s a great way to learn jQuery!

For this tutorial, we will use a 2D image square-sliding game, which I will go over line by line to demonstrate the train of thought behind the code.

A jQuery plugin is a perfect way to create image slideshows, custom user interface controls and of course browser-based games. We won’t just write JavaScript code here, we will create a jQuery plugin.

A plugin is nothing more than our own custom jQuery method. You know how we have jQuery’s methods .css() and .animate()? Well, jQuery gives us the ability to extend its own functionality with custom methods that we create ourselves. Like the existing jQuery methods, we can apply the method we will create to a jQuery selector.

Well, the game is called Fifteen, and we want to make our game “embeddable” inside an arbitrary HTML element like <div id = "target">here</div> so we can move it around anywhere on the page (Different sites have different layouts, wouldn’t you like to have the ability to embed your jQuery game/plugin into an arbitrary DIV element on any site?)

Let’s get started with the jQuery code.

We will actually create our own jQuery method and call it .fifteen(). Therefore, in order to launch the game inside an HTML element with the id “target” we will call this command:

$("#target").fifteen(128);

This will create and attach the game board to the div whose id is “target.” Also, each square will become 128 by 128 pixels in width and height based on the only passed parameter.

Executing a custom method as shown in the code above will pass the selector string “#target” to our plugin function which grabs the DIV. Inside our custom method, we can refer to that selector/element using the this keyword. And we can also enable jQuery methods on it by passing it to the new jQuery object like so:

$(this);

In order to create our own jQuery plugin we will use the $.fn.extend() method.

We can also refer to this as extending functionality of the jQuery library with our own methods

Let’s take a look:

$.fn.extend({
  fifteen: function (square_size) {
    // Game code goes here…

    // Example — set the width of all\
    // DIVs to what was passed as square_size
    $("div").css({width: square_size});

    // The basics of the inner-workings of a plugin:
    // refers to the passed selector,
    // eg: $("#target")
    // same thing, but with jQuery methods enabled
    var a = this;                      
    var b = $(this);            

    // Let’s do something with the HTML
    // element passed as the CSS selector
    // paint all text inside "#target" red
    this.style.color = "red";
    // same exact thing, using jQuery method .css()  
    $(this).css("color", "red");
  }
});

That’s it! This is the basic structure of any jQuery plugin.

The code examples I use above are just examples that show how the basics work internally. But we will write an entire game within the scope of the function we called “fifteen”.

Within the scope of the plugin the this object refers to what was passed in the selector. Also, the square_size parameter is just a custom, arbitrary parameter passed to our game.

We decide what the parameters should be, and if your game or plugin require more, simply define more parameters, as in:

$.fn.extend({
  fifteen: function(square_size, param2, param3, etc) {
    /*.. game code…*/
  }
});

Within the scope of your plugin you refer to parameters by the very same names.

In our case square_size is the only parameter. It defines the arbitrary size of the square in the game. I use 128 (as in 128 pixels in width and height). The grid is 16 by 16, but there are only 15 movable squares, because one is always empty. However, we still create all 16 DIVs, and just make one white. We do this in order to simulate the shadow effect on an empty square.

Before we get into the code, let’s take a look at the game’s construction as far as HTML goes. You will notice that there are 16 squares and each square is a separate DIV element:

The Fifteen Game in Action

Well hello there…

You can see the game in action here.

The point of the game is to shuffle the image, one square at a time (by shifting it into the empty square, thus swapping locations) until the image is complete.

The white square is always empty. You are only allowed to shift squares that can physically be shifted into the empty spot, as if on a physical game board. This means only squares to the north, south, west and east of the empty square can be shifted. This test is performed within the stand-alone function I wrote and called Move() – it is separate from the plugin itself, it’s like a helper function.

Before we dive into the code, we need to briefly talk about some common problems we will face while designing a plugin-based game.

Embedding the Plugin Into Arbitrary DIVs (or other HTML elements)

Remember that when writing your plug-in it is wise to inject the HTML elements which are created as part of the game… into a parent DIV. The purpose of this is that your plug-in becomes embeddable into any arbitrary HTML element. Different websites have different layouts. So it’s important for our plug-in to be flexible. That it could be embedded into an arbitrary HTML element anywhere on the page. In this example I called that element:

<DIV id = "target"></DIV>

Moving around this element will physically move the game around anywhere on the page.

Injecting HTML into Empty Elements Using the .append(html) Method

We leave the div unpopulated, without any game HTML. Why’s that? This is because the plugin itself will generate the game’s HTML elements and insert them into the target DIV. No need to pre-type them “by hand.”

This is smart, because it keeps the parent element clean. But also, this example in particular allows us to explore the jQuery’s native .append(html) method. It literally appends new HTML at the end of a specified element. That is, you never even have to write HTML into the document itself, the .append() method injects HTML into the web page dynamically, you just specify the target element using a selector like:

// Add one div to the end of #board
$("#board").append("<div></div>");

Using :nth-child(index) Instead of IDs or Classes

I think it’s worth mentioning that we can refer to DIVs inside, say, a parent DIV as children. This is really helpful. For example, let’s say we have 16 DIVs (children) stored inside one parent DIV:

<div id = "board"> <!– This is called a "parent" element //–>
  <div></div>
  <div></div>
  <div></div>
  <!– … ok, imagine 16 DIVs here. These are the "children" of "#board" //–>
  <div></div>
  <div></div>
</div>

You don’t really need to assign 16 IDs to all 16 DIVs! jQuery takes care of that for us. For example, to refer to the 3rd DIV inside a parent DIV (in our game it’s #board,) instead, we can do something like this without ever assigning any classes or ids to any of the children:

// Hide 3rd DIV in the list of children.
//No need to assign an id to each one!
$("#board").children("div:nth-child(3)").hide();

Notice we used the .children() method to grab all children of a parent (DIVs inside parent,) and then we also specified that we need a 3rd child using the literal selector :nth-child(n) where n is the index of the DIV we need. Note, however that this index doesn’t start with 0 like JavaScript arrays. It starts with index of 1. :nth-child(0) will not select anything.

The Code

Well, I think we are ready to start writing the code. Let’s get to it.

// This is a JavaScript event.
// It means – once DOM is finished loading,
// execute everything inside the callback function scope
// This is where we initialize the game
$(document).ready(function()
{
  // Initialize the game and create the plugin

  // When the squares swap places, the moving square must always appear on top
  var zi = 1; // We increment z-index each time a square is shifted

  // The index of the empty square by default, the 16th square
  var EmptySquare = 16;

  // Now, this is where we create the plugin and call it fifteen.
  $.fn.extend({
    fifteen: function(square_size) {
      // Grab the id of the HTML element into which we are placing the game,
      // it is the selector – "#target" from  $("#target").fifteen(128);
      var targetElement = "#" + $(this).attr("id");

      var sqSize = square_size + ‘px’;
      var boardSize = (square_size * 4) + ‘px’;

      // Inject DIV into target, this is our game board
      $(targetElement).html(
        "<div id = ‘board’></div>"
      );

      $("#board").css({
        position:‘absolute’,
        width: boardSize,
        height: boardSize,
        border: ‘1px solid gray’
      });

      // Populate the game board’s HTML container with 15 squares
      for (var i = 0; i < 16; i++)
      {
        // A  dirty way to create an arbitrary DIV and
        // append it into HTML dynamically
        // Notice each square uses the same image monalisa.jpg
        // It just uses a different x/y offset for each square
        $("#board").append(
          "<div style=’" +
            "position: absolute;" +
            "left: " + ((i % 4) * square_size) + "px;" +
            "top: " + Math.floor(i / 4) * square_size + "px;" +
            "width: " + square_size + "px;" +
            "height: " + square_size + "px;" +
            "text-align: center;" +
            "line-height: 128px;" +
            "-moz-box-shadow: inset 0 0 20px #555555;" +
            "-webkit-box-shadow: inset 0 0 20px #555555;" +
            "box-shadow: inset 0 0 20px #555555;" +
            "background: #ffffff url(monalisa.png) " +
              ((i % 4) * square_size) + "px "
              + Math.floor(i / 4) * square_size + "px " +
              "no-repeat !important’>" +
        "</div>");
      }

      // Empty up the 16th square, as the starting point
      // EmptySquare = 16
      $("#board")
        .children("div:nth-child(" + EmptySquare + ")")
        .css({
          backgroundImage: "",
          background: "#ffffff"
        });

      // Attach click event to each square
      $("#board").children("div").click(function() {
        Move(this, square_size);
      });
    }
  });

  // Move() is the function that is called when a square is clicked
  // Note that it is independent of the plugin itself which is described above
  // It takes two parameters,
  //     1. object handle to the square that was clicked, and
  //     2. the width of the square
  function Move(clicked_square, square_size)
  {
    // We need to locate movable tiles based on where the empty spot is,
    // We can only move the four surrounding squares
    var movable = false;

    // Swap x/y between the clicked square and the currently empty square
    var oldx = $("#board")
      .children("div:nth-child(" + EmptySquare + ")")
      .css("left");
   
    var oldy = $("#board")
      .children("div:nth-child(" + EmptySquare + ")")
      .css("top");

    var newx = $(clicked_square).css("left");
    var newy = $(clicked_square).css("top");

    // The clicked square is north of the empty square
    if (oldx == newx && newy == (parseInt(oldy) square_size) + ‘px’) {
      movable = true;
    }

    // The clicked square is south of the empty square
    if (oldx == newx && newy == (parseInt(oldy) + square_size) + ‘px’) {
      movable = true;
    }
       
    // The clicked square is west of the empty square
    if ((parseInt(oldx) square_size) + ‘px’ == newx && newy == oldy) {
      movable = true;
    }
       
    // The clicked square is east of the empty square
    if ((parseInt(oldx) + square_size) + ‘px’ == newx && newy == oldy) {
      movable = true;
    }
   
    if (movable)
    {
      // Increment zindex so the new tile is always on top of all others
      $(clicked_square).css("z-index", zi++);

      // Swap squares… Animate new square into old square position
      $(clicked_square).animate({ left: oldx, top: oldy }, 200, function()
      {
        //Move old square into new square position
        $("#board")
          .children("div:nth-child(" + EmptySquare + ")")
          .css("left", newx);
       
        $("#board")
          .children("div:nth-child(" + EmptySquare + ")")
          .css("top", newy);
      });
    }
  }

  // Ok, we’re ready to initialize the game, let’s do it.
  // Create a game with 128 by 128 squares inside "#target" div:
  $("#target").fifteen(128);
});

With the page looking like:

<html>
  <body>
    <!– This is where the game will be injected //–>
    <div id = "target"></div>
  </body>
</html>

I tried to explain the code to the best of my ability here but some details were skipped because there is so much more to JavaScript.

If you feel like you still need to understand what some of the things mean I recommend grabbing a copy of my jquery tutorial book which contains a multitude of tutorials and visual diagrams. They are designed to help you build your understanding of JavaScript and jQuery so you can write your own plugins, games, etc.

I hope you enjoyed this tutorial and perhaps learned a thing or two.

Leave a Reply

Your email address will not be published. Required fields are marked *