Warning: The magic method __toString() must have public visibility and cannot be static in /home/darrent/bostongamejams.com/wp-content/plugins/inline-google-docs/inc/gelement.php on line 129
Akihabara Tutorial, Part 7: Game Logic and HUD — Boston Game Jams

Akihabara Tutorial, Part 7: Game Logic and HUD

by Darren Torpey on August 4th, 2010

This is the seventh tutorial in a multi-part tutorial series where we will teach you how to make an 8-way shooter in HTML5 and JavaScript using the Akihabara framework. Akihabara is a set of Javascript libraries that take advantage of some of HTML5’s unique features to facilitate game creation. One of the best things about writing a game in HTML5 is that it will run in any browser that supports HTML5 on any platform. This includes Chrome, Firefox, Safari, and WebKit browsers on iPhone/iPad, WebOS devices, and other mobile platforms.

In this tutorial, we’ll turn the elements we’ve developed so far into a proper game by adding victory and loss conditions and providing a simple heads-up-display (HUD for short) so that players can track their progress.

The final product

In the end we’re going to have something that looks like this. Use the Z key to advance past the title screen, and use the arrow keys to move around. You’ll see a timer that counts down: when it reaches zero, you win. If you touch one of the enemies, you lose.

Count-down timer

In order to track how much time has passed, we add two  globally-scoped variables to the top of the script, right after the place where we define our other global variables like maingame and frameCount:

var TIME_LIMIT = 10; // This will determine how many seconds the player must survive to win
var timeStarted;     // This will record the current time when the game started

The first sets the number of seconds that the player will need to survive to trigger the “win” state, and the second creates a timeStarted global variable that will hold the time at which the game initialized.  We record that time in the maingame.initializeGame function, which is defined in our main function:

function main() {
  ...
  maingame.initializeGame = function() {
    ...
    // This gives us the time at which the game started,
    //  in milliseconds since the epoch
    timeStarted = (new Date()).getTime();

The getTime function is built into Javascript, and it returns the time in milliseconds since the epoch (midnight, Jan 1 1970). This is run once, when the game begins, and so timeStarted serves as a reference point.

Now we need to keep track of how much time has passed since timeStarted. Every iteration, we’ll check how much time has passed since the game began, and if it’s equal to the TIME_LIMIT value we set above, we’ll show a victory screen. We’ll put this logic in the map object, which will keep this tutorial simple while still ensuring this calculation is done before any other game logic executes.

function addMap() {
  gbox.addObject({
    ...
    blit: function() {
      ...
      // Figure out how many seconds have elapsed since the game started
      //  we divide by 1000 because the number given is in milliseconds
      secondsElapsed = ((new Date()).getTime() - timeStarted) / 1000;

      // If we're reached our TIME_LIMIT, show the victory screen
      if (secondsElapsed >= TIME_LIMIT)
        gameOverWin();

So now all we have to do is write the gameOverWin function. This will be a very simple function because we’re hooking into some pre-built functionality in Akihabara:

function gameOverWin() {
  maingame.setState(801);
}

This goes right after our loadMap function definition. We won’t explain how this works in depth, but it comes from the gamecycle.js file in Akihabara. It resets the background music channel, deactivates all groups except for the main game itself, and plays the default gameEndingIntroAnimation.  If you run the game now, you’ll see that after 10 seconds pass, it’ll show a simple victory screen. Hurray!

Heads-Up Display

That’s nice and all, but it’d be much nicer if we could see how many more seconds we need to survive in order to win, wouldn’t it? So let’s add a simple HUD element that shows how many seconds are left on the timer. This will be quite easy because Akihabara comes with some nice functions to help us along.

First, we’ll add a HUD element in our maingame.initializeGame function:

funcction main() {
  ...
  maingame.initializeGame = function() {
    ...
    // This adds a "label" widget, with "small"-font text to the screen
    //  located 25 pixels down from the top of the screen and
    //  40 pixels back from the right side of the screen
    maingame.hud.setWidget('time_left', {
      widget: 'label',
      font:   'small',
      value:  0,
      dx:     gbox.getScreenW() - 40,
      dy:     25,
      clear:  true
    });

Now we’ll return to our secondsElapsed-related code and update our timer HUD element based on that information. Here’s all the new code for addMap, including finding the seconds since we began, writing that value to the HUD, and checking to see if we win:

function addMap() {
  gbox.addObject({
    ...
    blit: function() {
      ...
      // Figure out how many seconds have elapsed since the game started
      //  we divide by 1000 because the number given is in milliseconds
      secondsElapsed = ((new Date()).getTime() - timeStarted) / 1000;

      // Calculate the number of full seconds that have passed, subtract that
      //  from the total number of seconds the player must survive, and
      //  update the timer HUD element with that value, then tell the HUD to redraw
      maingame.hud.setValue('time_left', 'value', Math.ceil(TIME_LIMIT - secondsElapsed));
      maingame.hud.redraw();

      // If we're reached our TIME_LIMIT, show the victory screen
      if (secondsElapsed >= TIME_LIMIT)
        gameOverWin();

Now run the game again, and you’ll see a clear representation of how much time is left on the clock before you win.

Triggering code on collision

Next we want to write some code that causes a “game over” when the player sprite touches an enemy sprite. Fortunately, Akihabara has the toys.callInColliding function that we’re going to use for this very purpose. The function tests to see if a given instance of an object is colliding with any object in a given group; if it is, then it calls a function you specify. This is great to test if a player object is colliding with anything in the “enemy” group. Unfortunately, as of version 1.2.1 the function does a point-to-box check, and not a box-to-box check, meaning that it only checks the origin pixel of the object against the boxes of the group. So we need to write our own. Fortunately it only requires slight modification from the original function in toys.js.

Here’s our new callInColliding function, which we’re renaming to callWhenColliding, and are putting in our code right after our loadMap function definition:

function callWhenColliding(obj,group,call) {
  for (var i in gbox._objects[group])
    if ((!gbox._objects[group][i].initialize)&&toys.topview.collides(obj,gbox._objects[group][i]))
      if (gbox._objects[group][i][call]) {
        gbox._objects[group][i][call](obj);
        return i;
      }
  return false;
}}

The function accepts three arguments: obj, group, and call. The first argument is the object that we’re testing collision against. The second argument is the group of other objects that we’re testing collision against. And the call argument is the name of the function that we’re going to call if we are colliding; the function that call refers to must live inside the objects described by the group. The way it works is it iterates through each object in the given group and does a collision test against it and the obj object. If there’s a collision, it calls the function call and passes it the obj object as an argument. The biggest difference between this function and the core Akihabara function is that it calls toys.topview.collides (box-to-box) instead of toys.topview.pixelcollides (point-to-box). We also cleaned up how some of the arguments are accessed.

For our game we want to test if our instance of the Player object is colliding with any object inside the “enemy” group (in this case, any of our Enemy objects).  If we’re colliding, we want to go to the game over state. The first thing we do is call callInColliding from inside the Player object’s first function, so it makes the check every frame.

first: function() {
  // ...

  toys.topview.handleAccellerations(this);
  toys.topview.applyForces(this);
  toys.topview.tileCollision(this, map, 'map', null, { tollerance: 6, approximation: 3 });

  // New code for Part 7
  callWhenColliding(this, 'enemy', 'gameOverFail');

},

In this case we put the code right after our collision handling functions. We pass it this, which is the Player object that we’re calling the function from. Then we pass it the string ‘enemy’, referring to to enemy group. Finally we pass it the string ‘gameOverFail’, which will be the name of the function that we call to set the game over state.

Next we go to the Enemy object description and we add a new function, right after our blit description:

    
blit: function() {
  gbox.blitTile(gbox.getBufferContext(), {
    tileset: this.tileset,
    tile:    this.frame,
    dx:      this.x,
    dy:      this.y,
    fliph:   this.fliph,
    flipv:   this.flipv,
    camera:  this.camera,
    alpha:   1.0
  });
},

gameOverFail: function() {
  maingame.setState(700);
},

This is similar to our gameOverWin function, except we set the state to 700 instead of 801. It resets the background music, deactivates everything but the game object itself, and then runs the gameoverIntroAnimation.

Game over man, game over

Load up your index.html in a browser and look at the functionality. Hey, whaddya know — we have a working game!  If you manage to avoid the enemies until the counter hits 0, you get a screen that says “Congratulations!” If you get hit by an enemy in the meantime, you lose.

This tutorial wraps up the “basic” arc of our series. We haven’t taught you how to make an 8-way shooter yet, but we have taught you how to make a fairly simple game, all the way from title screen to game over. Our next set of tutorials is going to be more advanced, covering things like audio, mouse input, and other topics that are going to require writing a lot of custom code beyond what Akihabara provides on its own.

Akihabara Tutorial Series

Written by Darius Kazemi and Darren Torpey

{ 9 comments… read them below or add one }


Fatal error: Cannot assign by reference to overloaded object in /home/darrent/bostongamejams.com/wp-content/themes/thesis_18/lib/classes/comments.php on line 176