The code

Now that you are familiar with the HTML5 APIs used in this fun game, let's look under the covers and see how the game was put together. Due to brevity and ease of explanation, only the main portions of the source code for this game will be listed or explained here. Be sure to download the complete source code for the game at the book's website.

The HTML structure

The first component of this game was the HTML structure. The main pieces of it are the tracks where each player moves, along with each individual player, and the containers that show the text that needs to be typed by the user. There is also a second container that displays whatever text the player actually types in. For customization, there is an input type range that allows the player to change the difficulty level of the game, which for all practical purposes, only increases the speed attribute of the enemy player.

<section class="tracks">
  <div class="track">
    <span data-name="badGuy" data-speed="0"></span>
  </div>

  <div class="track">
    <span data-name="goodGuy" data-speed="0"></span>
  </div>
</section>

<section id="mainContainer">
  <div id="wordsToWrite"></div>
  <div id="wordsWritten"></div>
  <button data-intent="play">Play</button>
</section>

Other elements only add to the experience, either visually (through animations and other visual components) or with more interaction. But these are the basic game components without which the game cannot work.

JavaScript and logic

The logic for this game is separated into three very basic components, namely, a Player class that encapsulates the behavior of each player, a game loop function that is called at a regular interval based on a game timer, and a few global functions that encapsulate various pieces of logic used throughout the life cycle of the application.

The Player class holds a reference to the DOM nodes that represent the player, the track where the player runs, and defines some basic behavior to control the player.

function Player(query) {
  // Hold a reference to the DOM element that they playerwill control
  var element = document.querySelector(query);
  var trackWidth = parseInt(element.parentElement.offsetWidth);
  var minLeft = 0 - parseInt(element.offsetWidth / 2);
  var maxLeft = trackWidth - parseInt(element.offsetWidth / 2);

  // Move the player based on whatever speed is set inits custom data attribute
  this.move = function() {
    var left = parseInt(element.style.left);
    var speed = parseInt(element.attributes.getNamedItem("data-speed").value);

    element.style.left = (left + speed) + "px";

    if (left > maxLeft) {
      this.moveToFinish();
    } else if (left < minLeft) {
      this.moveToStart();
    }
  };

  // Manually move the player to a certain point along its track,independent of
  // what its speed data attribute is.
  this.moveToPercent = function(percent) {
    element.style.left = parseInt(percent * maxLeft) + "px";

    if (percent >= 100) {
      this.moveToFinish();
    } else if (percent <= 0) {
      this.moveToStart();
    }
  };

  // Determine if the player has reached the end of its track
  this.isFinished = function() {
    return parseInt(element.style.left) >= maxLeft;
  };

  // Place the player at the beginning of its track
  this.moveToStart = function() {
    element.style.left = this.getMinLeft() + "px";
  };

  // Move the player to the very end of its track
  this.moveToFinish = function() {
    element.style.left = this.getMaxLeft() + "px";
  };
}

In the global scope, we create two instances of this player class, one representing the hero of the game and the other the enemy that we try to defeat.

var hero = new Player("[data-name='goodGuy']");
var enemy = new Player("[data-name='badGuy']");

When the game starts, we initialize some code, which is among a few other things, means that we determine how fast the game timer is to run, how fast the enemy player is to move, what phrase the user is to type in, and most importantly, we register a keyboard event on the body element of our HTML structure. In other words, we listen to every key press anywhere on the page, so that we can detect what the user has typed in after the game has begun.

This is probably the most complex function in the game because we have to handle every key press by ourselves. This means that we need to take into account whether or not the user has pressed a key while holding the Shift key (in which case they have entered a capital letter), or whether a special key combination was pressed. For example, if the user presses the Backspace key, by default the browser will respond to this event by navigating the web page back to the last page navigated. Obviously, we don't want the browser to navigate away from our game. Instead we want the last character typed in by the user to be deleted. Thus, small details such as this must be taken into account.

Finally, at each letter typed in by the user, we must check whether that letter was the same letter we were expecting (the correct letter) or whether the user has typed in a wrong letter. Based on this decision, we output to the screen the letter just typed, along with some HTML that allows us to render that letter differently based on whether or not it was the right key.

function handleKeyPress(event) {

  var keyCodes = {
    SHIFT_KEY: 16,
    BACKSPACE_KEY: 8, 
    SPACEBAR_KEY: 32, 
    COMMA_KEY: 188, 
    PERIOD_KEY: 190
  };

  var wordsLen = wordsWritten.children.length;

  // If the Shift key was entered, just ignore it
  if (event.keyCode == keyCodes.SHIFT_KEY)
    return false;

  // If the backspace key was entered, don't let thebrowser navigate away
  if (event.keyCode == keyCodes.BACKSPACE_KEY) {
    event.preventDefault();

    // If we have deleted every letter entered by the user,don't do anything else
      if (wordsLen < 1)
        return false;

      // If the user has pressed the backspace key, andthere is at least one letter
      // that the user had typed in before, delete thatletter from where it was output.
      // Note that some browsers might not supportthe .remove() function on a node,
      // but rather use the removeChild() function onthe node's parent element.
    wordsWritten.children[wordsLen - 1].remove();
    return false;
  }

  // Determine what character the user has typed in
  var letter = String.fromCharCode(event.keyCode);

  // If the charactered enterd by the user is a letter,capitalize it if the Shift key was pressed
  if (!event.shiftKey && event.keyCode >= 65 &&event.keyCode <= 90)   
    letter = String.fromCharCode(event.keyCode + keyCodes.SPACEBAR_KEY);

  // Convert special character codes into their corresponding character
  if (event.keyCode == keyCodes.COMMA_KEY)
    letter = ",";

  if (event.keyCode == keyCodes.PERIOD_KEY)
    letter = ".";

  // Determine if they letter entered is right or wrong,and print special HTML to
  // indicate that. Move the hero if the letterentered was correct.
  if (letter == words[wordsLen]) {
    wordsWritten.innerHTML += "<span class='correct'>"+ letter + "</span>";
    var correct = document.querySelectorAll("#wordsWritten .correct").length;
    var percent = correct / words.length;
    hero.moveToPercent(percent);
  } else {
    wordsWritten.innerHTML += "<span class='wrong'>"+ letter + "</span>";
  }

  // By returning false from a key press event,we further prevent the browser
  // from taking any default action based on thekey combination entered.
  return false;
}

At every tick of the main game timer, which we can make faster or slower based on the difficulty that the player has selected, we call our main game loop, which performs some basic tasks. First we move each player, then check if any of them has reached the end of their track. This is done by calling each player's isFinished() function, defined by the Player class. If that is the case, then we know that the game is over. If that is so, we unregister the keyboard event we had bound to the body element, so that we no longer inspect any keyboard input from the user. After we've determined that the game is over, we determine if the user has actually won or lost the game. If they have won, we record their victory. If they have lost, we tell them so, and allow them to play a new game. If the game is not yet over, we simply wait for the game loop to run again.

function tick() {

  hero.move();
  enemy.move();

  if (isGameOver()) {
    document.body.removeEventListener("keydown", handleKeyPress);

    if (hero.isFinished()) {
      gamesWon++;
      showWinPanel();
    } else if (enemy.isFinished()) {
      showLosePanel();
    }
  } else {
    setTimeout(tick, tickPeriod);
  }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.136.84