CHAPTER 12 ■ VIDEO GAMES
// Once a second, reset the tick counter
// to keep it from overflowing the value of an int
if (ticks == 50) {
ticks = 0;
}
// Move the left-most row every tick
row1.tick();
/ Move the center row every second tick
if (ticks == 0 || ticks % 2 == 0) {
row2.tick();
}
// Move the right-most row every third tick
if (ticks == 0 || ticks % 3 == 0) {
row3.tick();
}
// Update the location of the player's cursor every tick
shooter.tick();
}
}
As with the TargetClickPanel class, most of the ShootingGalleryPanel class sets up and manages the
playing area (the user interface). The actionPerformed method, however, consists entirely of game logic
and contains the game loop. It works by moving the rows every so many ticks. The left-most row (which
contains the smallest and most valuable targets) updates on every tick, making it very fast (I have a hard
time hitting those, but that's how it should be). The middle row updates on every second tick, and the
right-most row (containing the largest and least valuable targets) updates every third tick. Consequently,
the big and slow targets are in the way of the speedy and valuable targets, as is typical of shooting gallery
games.
Finally, the actionPerformed method updates the location and status of the cursor. The cursor is the
only thing the user controls, so we have to update its location and status as often as possible. Fifty times
a second is enough to keep up with the actions of any player I've ever met.
Once the timer has run for a full second, the actionPerformed method resets it. Otherwise, a long
game might see the number of ticks exceed the maximum possible value of the ticks’ variable. That's
unlikely, but why not head off a potential problem before it starts? We could just as easily reset every
thousand or every million ticks, but setting an int to 0 is a very cheap operation, so I chose to reset every
second.
The actionPerformed method handles all three of the tasks a game loop needs to do, but not in the
same order or in the same way as we saw in the TargetClick game. This time, the updating of the
interface is pushed off to other classes (ShootingGalleryTargetRow and ShootingGalleryShooter), and
the handling of the user input comes last rather than first. In this case, whether the user input gets
handled first or last makes no difference. I did it this way to demonstrate that the order of the tasks
doesn't usually matter. There are exceptions, but you'll be able to identify those when you encounter
them, as they will be part of how your game works.
Since one of the three main functions of the ShootingGalleryPanel.actionPerformed method is to
update the rows, let's look at how the rows work (the other main functions are updating the cursor and
managing the ticks). As you read the code for the ShootingGalleryTargetRow class, pay particular