Tube: Technical stuff

Tube was a lot less complicated to build than most of my other recent games. It is roughly on par with Don's Dugout or Adios, Amoebas!, which is not to say it was entirely trivial.

Normally in a game like this you would use a 2D array to internally represent the playing field -- in our case with a block size of 24x24 pixels. But that won't work for Tube, because the pipe overlaps itself in some places, so the playing field essentially gets a third dimension. Although it might be possible to use a 3D array instead, it is far more efficient to represent the tube with some sort of linked list.

Each element in that list represents a block and can have a maximum of four neighbors -- above, below, to the right and to the left. I also let the list contain information about the screen co-ordinates of the block, whether it is obscured and where its nuts and bolts are, if it has any.

You may have noticed some elements of artificial intelligence in the game. To be more precise,

  • the monsters following the player around,
  • scared monsters avoiding the player,
  • zapped monsters finding the quickest path to the exit.
Starting with the last one, I achieve that by assigning an integer to each block of the maze. First I give both the exit tubes the value 0. Then I give their immediate neighbors the value 1. Then I find all their neighbors that haven't been numbered yet and give them the value 2, and so on. The image to the right illustrates this.

In order to get to the exit, the monsters just need to keep track of which block they are located at and move to the neighboring block that has the lowest number. (The numbering is done before a level starts, so it doesn't slow the game down.) I keep a similar number for each cell telling how far it is from the player's current location, but not for the entire maze -- just within, say, 15 blocks. That saves a bit of time. Scared monsters obviously try to get to the neighboring block with the highest player distance number.

Only the red and green monsters actually chase the player. The blue ones are more harmless in that sense and just follow a simple rule: pick any available direction at random, except back to where you came from, unless there is no other way. It works pretty well and even gives the illusion of purpose.

Mobile objects going under pipes or into covered segments took a bit of extra work that wouldn't have been necessary if only the "clipping rectangle" functionality of Java had been fully reliable. Instead, I had to do the following.

First I copy a square portion the same size as the mobile object to an offscreen place, using the copyArea method. Having determined how much of the object is supposed to be obscured, I draw the entire object on the offscreen portion and only copy back the part that should be visible.

To save some time, I skip that three-step procedure when the object is either fully visible or fully obscured. Then I either draw the object directly on the playing area or not at all. Those situations make up the vast majority, so very few extra graphics operations need to be performed during an average animation frame.