You’re now going to take the example to another level
entirely by adding a means of controlling the UFO with keys on a mobile
phone, as well as adding some tumbling asteroids as obstacles for the
UFO to dodge. I’d stop short of officially calling the UFO 2 example a
game, but it’s about as close as you can get!
The UFO 2 example program involves the following major additions with respect to the original version of the program:
User input for controlling the flying saucer
Three frame-animated asteroid sprites that float around the screen
Collision detection between the flying saucer and the asteroid sprites
You are already well equipped to tackle all these additions, so let’s get to it.
Writing the Program Code
The MIDlet class for
the UFO 2 example doesn’t change at all from the previous version, so
let’s jump straight into the modified UFOCanvas class. The first change is the addition of three asteroid sprites, which are stored in a Sprite array:
private Sprite[] roidSprite = new Sprite[3];
You initialize these asteroid sprites in the start() method of the MIDlet by using the following code:
Image img = Image.createImage("/Roid.png");
roidSprite[0] = new Sprite(img, 42, 35);
roidSprite[1] = new Sprite(img, 42, 35);
roidSprite[2] = new Sprite(img, 42, 35);
As you can see, the asteroid image (Roid.png)
is created only once, and then used as the sprite image for all three
asteroid sprites. In addition to creating the three asteroid sprites,
the start() method also initializes the position of the flying saucer sprite differently than the original version did:
ufoSprite.setPosition((getWidth() - ufoSprite.getWidth()) / 2,
(getHeight() - ufoSprite.getHeight()) / 2);
Although it looks a bit
messy, this code is simply centering the flying saucer sprite on the
screen so that it isn’t initially colliding with the asteroids, which
are all three initially located in the upper-left corner of the screen
at the default position (0,0).
The update()
method is where most of the interesting new code lies in the UFO 2
MIDlet. All the key handling takes place in a single block of code,
which looks like this:

Nothing too
tricky here! The code simply checks the four directional keys and alters
the UFO speed accordingly. Notice that the speed is still capped at a
magnitude of 8 regardless of how much you pound on the keys. After
altering the speed, the flying saucer is updated with another couple of
lines of code:
ufoSprite.move(ufoXSpeed, ufoYSpeed);
checkBounds(ufoSprite);
The familiar sprite move() method is called to move the saucer, whereas the new checkBounds() method is called to perform any screen wrapping. The code in the checkBounds()
method isn’t new, but isolating it in a reusable method is. This is
important because you need to be able to check the bounds of the UFO and
asteroids sprites, and it wouldn’t make much sense to duplicate the
code unnecessarily.
The updating of the
asteroid sprites takes place in a loop that takes care of several
different tasks. Here’s how the loop starts:
for (int i = 0; i < 3; i++) {
The first task within
the loop is to move the asteroids and check their bounds in case they
need to be wrapped around the screen:
roidSprite[i].move(i + 1, 1 - i);
checkBounds(roidSprite[i]);
The only tricky code
here is the amount by which the asteroids are moved. To help give the
asteroids their own unique speeds, the index of each asteroid is used as
the basis for determining how fast it moves and in what direction. A
similar approach is used to vary the animation frame directions of the
asteroids, as this code reveals:
if (i == 1)
roidSprite[i].prevFrame();
else
roidSprite[i].nextFrame();
The idea behind this
code is for the second asteroid to spin in an opposite direction as the
first and third asteroids. You can accomplish this easily by moving the
animation frames of the second sprite in the opposite direction as the
others.
The remaining code in
the asteroid update loop has to do with detecting a collision between
the flying saucer and any of the asteroids:
If a collision has indeed occurred, the AlertType object is used to play a standard error sound, which is determined by each individual phone. A collision
in this program triggers a reset of the sprite positions, as is evident
in the code. If this were a full-blown game, this is where you would
decrement the number of lives and check to see whether the game is over.
In this case, you just reposition the sprites and let the animation
keep on trucking.
There is only a minor change to the draw() method in this version of the UFO example MIDlet. I’m referring to the code that draws the asteroids, which looks like this:
for (int i = 0; i < 3; i++)
roidSprite[i].paint(g);
That wraps up the new chunks of code in the UFO 2 example. Listing 1 contains the complete code for the new UFOCanvas class, which helps add some context to the code you just learned about.
Listing 1. The UFOCanvas Class Serves as a Customized Canvas for the UFO 2 MIDlet
You’ve already learned the ins and outs of this code, so I’ll spare you more discussion. Let’s see how it actually runs instead!
Testing the Finished Product
Testing the UFO 2
example MIDlet is considerably more interesting and entertaining than
previous examples because you now get to use keys to control an animated
graphical object (flying saucer). Figure 1 shows the UFO 2 MIDlet in all its glory as I carefully steer it around those pesky asteroids in the J2ME emulator.