Research & Development
GMP Neko Part 1
Click anywhere to move the mouse, then watch Neko chase it!

Overview

This tutorial will take your understanding of Trevor Cowley's GMP game engine to the next level after you have completed the basic Hello World Tutorial on the GMP site.

You will implement the well known Neko game in javascript using some more advanced features of the GMP engine, as well as some other miscellaneous game development tips and techniques along the way.

Because this is a long tutorial, we'll break it into two parts:

Prerequisite

This tutorial assumes that you have completed the Hello World Tutorial GMP site. If you understand that application, this page will be a natural progression for you.

About Neko

Neko was a small Macintosh animation/game written and drawn by Kenji Gotoh in 1989. "Neko" is Japanese for "cat," and the animation is of a small kitten that chases the mouse pointer around the screen, sleeps, scratches, and generally acts cute. The Neko program has since been ported to just about every possible platform, as well as rewritten as a popular screensaver.

For this example, you'll implement the game using the GMP Engine by Trevor Cowley using the original Neko graphics.

You need to set up an HTML file to display the game as shown. The important things are to:

  1. include the gmp-engine javascrpt file ahead of your code, and
  2. create an empty <div id="neko-game"> to hold the game.

Notice that I have commented out the links to the game's external stylesheet and javascript files.

While coding, I find it easier to just edit everything in one place (i.e. the html file), because the browser stubbornly caches external assets.1

As you follow along, put all your javascript between the <script></script> tags and your styles inside <style></style> tags. Then you can simply refresh the browser (press F5 in Windows) to see your changes.

<!DOCTYPE HTML>
 <html>
 <head>
    <!-- link rel="stylesheet" href="neko.css" -->
    <style>
       // during development, put your styles here
    </style>
 </head>
 <body>
  <div id="neko-game"></div>
    <script src="gmp-engine.1.7.4.js"></script>
    <!-- script src="neko.js"></script -->
    <script type="text/javascript">
       // during development, javascript goes here
    </script>
 </body>
 </html>

When the program development is finished, simply copy the contents of the <style> tag to neko.css, and the <script> tag to neko.js and uncomment the links.


1You can read about this caching problem on stackoverflow amoung other places. The www.quirksmode.org solution — enter the URL of the .js file in the location bar, load it, hit Reload — works; but do you really want to go through all that while you're developing?

Step 2 — Create A Game Shell

We'll start with a basic shell that we can build on as we go. All of this should look familiar if you've gone through the Hello World Tutorial{target="_new"} on the GMP site{target="_new"}, so the explanation will be brief. I'll just point out differences specific to Neko.

Relative Positioning

The first point has to do with positioning the game on the page. In the GMP tutorial, the "hello world" app was absolutely positioned at the upper left corner of the page with this viewport definition:

  G.makeGob('viewport', G)
   .setVar({x:50, y:50, w:200, h:325});

In order to relatively position the game on the page set nextStyle:{position:'relative'} on the viewport Gob, after specifying its parent element (i.e. the game's <div>) in the makeGob call as shown.

The Game Sprites

For this game there will be two primary game objects — the cat, Neko, and the mouse he is chasing. In conventional game terminology these are the sprites, but in GMP they are just "game objects" or Gobs.

To avoid confusion I've named the mouse Gob mickey since we will also be using the computer's mouse for input. From now on in this tutorial, mouse will refer to the computer's mouse, and mickey will refer to the mouse that Neko is chasing.

We need to create a main AI method, and an AI method for each of these sprites. At this point they just draw themselves — we'll implement the game logic below.

A Word About Debuging

I'm a bit old school when it comes to debugging — I like to dump variables to the screen from within the program. This lets the game loop run. So I've added a debug panel that can be deleted when the app is completed.

Right now the mainAI method displays the locations of Neko and Mickey (which were very useful while developing this game). When we get to the mouse handling, you might find it useful to dump the mouse location (as I did) — Or not, it's up to you!

G.F.loadMain = function () {
  this.AI = G.F.mainAI;

  var vp = G.makeGob('viewport',G,'div',
     document.getElementById('neko-game'))
   .setVar({w:300, h:350,
     nextStyle:{position:'relative'}})
   .setStyle({backgroundColor:'#ddd'})
   .turnOn();

  G.makeGob('neko', G.O.viewport)
   .setVar({x:(vp.w-32)/2, y:vp.h-150, z: 101,
            w:32, h:32, AI:G.F.nekoAI})
   .setStyle({backgroundColor:'#09f'})
   .turnOn();

  G.makeGob('mickey',G.O.viewport)
   .setVar({x:16,y:16, z:100,
            w:16,h:16,AI:G.F.mickeyAI})
   .setStyle({backgroundColor:'#f00'})
   .setState({moves:0})
   .turnOn();

  G.makeGob('debug',G.O.viewport)
   .setVar({x:0, y:vp.h-50, w:vp.w, h:50})
   .setStyle({backgroundColor:'#09f'})
   .turnOn();
  }

G.F.mainAI = function () {
  G.O.mickey.AI();
  G.O.neko.AI();
  // debugging
  var n = G.O.neko, m = G.O.mickey;
  var msg = "Neko: " + n.x + ", " + n.y +
     "<br> Mickey: " + m.x + ", " + m.y;
  G.O.debug.setSrc(msg).draw();
  return this;
  };

G.F.nekoAI = function (){
  this.draw();
  return this;
  };

G.F.mickeyAI = function () {
  this.draw();
  return this;
  };

G.makeBlock('main', G.F.loadMain).loadBlock('main');

Step 3 — Mouse Handling

We need to detect mouse clicks in order to place mickey in a new location. GMP offers two basic approaches that are exposed through the _Global Mouse Input Manager Object_, G.M.

The first way is to use a tradional mouse handler to hook mouse events instaneously as as they occur. The other approach is much more convinient: GMP catches the mouse events for us and sets a flag which can be polled during the subsequent game loop iteration.

We'll use the polling approach because it leads to much cleaner code that adheres more closely to accepted game development practices2. To detect a mouse press we simply check whether G.M.wasPressed is true. If so, then G.M.up.x and G.M.up.y cache the location of the press (for the duration of the current game loop iteration). What could be easier?

Well, there is one slight caveat — The mouse press could have occured any place on the current page, not necessarily on our app. Because of this, the G.M.up coordinates are measured from the upper-left corner of the web page, rather than the game.

For our purposes we are only interested in clicks on the game's viewport. Once we have such a click, we need to translate it's location into "viewport" coordinates (since these are what we use to specify Gob locations in GMP). At first, this seems terribly inconvinient, but it's simple to do and gives us quite a bit more flexibility as developers.

The key is that each Gob exposes the following method and properties:

Let's apply them to our game. In the code below, the mainAI only calls the mickeyAI after ensuring that a valid mouse click occurred. Otherwise we don't need to redraw Mickey.

G.F.mainAI = function () {
  if (G.M.wasPressed
  && G.O.viewport.tagContainsXY(G.M.up.x,G.M.up.y))
     G.O.mickey.AI();

  G.O.neko.AI();
  return this;
  }; 

Then the mickyAI sets its x and y location to the viewport coordinates of the click (by subtracting the absolute viewport location from the absolute mouse location) and calls draw().

G.F.mickeyAI = function () {
  if (this.on) {
     var vp=G.O.viewport;
     this.setVar({
        x:G.M.up.x-vp.docx,
        y:G.M.up.y-vp.docy
        }).draw();
     }
  return this;
  };

2Although there are similarities between dynamic web design and game development as pointed out by GMP author Trever Cowley, input handling is not one of them. In game development it is generally accepted that input processing is done as a distint process in the game loop to keep things orderly. See Onslaught Case Study for a nice explanation.