Table of ContentsThe Game ScreenAdding the Graphics

The Game Loop

At the heart of any action game is a processing loop used to carry out all the tasks that keep things moving. You need to move those trucks and cars, detect input from the player, check for collisions, and render everything to the screenand you need to do it quickly enough to give the player the impression that it's something like reality.

To do this, you use what's commonly termed a game loop. Essentially, you have the application run around like a dog chasing its tail as fast as it can. You then carry out any necessary tasks as part of this looping process. The number of times per second the dog completes a circle (and this is one quick dog) is known as the application's CPS (cycles per second). Figure 7.2 illustrates this concept.

Figure 7.2. The components of a simple game loop

graphic/07fig02.gif


To implement a game loop, you need an execution process independent of the application so that you can maintain control over it (such as pausing). In Java, you do this by creating a separate thread using the rather appropriately named Thread class.

Adding this to the class is pretty easy. First change the GameScreen class so that it implements the Runnable interface. The Runnable interface is required for a class, in this case the GameScreen class, to become a target of execution for a new thread.

public class GameScreen extends Canvas implements
Runnable

Next add the thread initialization process to the GameScreen constructor.

public GameScreen()
{
   …

   // create the game thread
   Thread t = new Thread(this);
   t.start();
}

This is simpler than it looks. First you construct the Thread object, assigning this instance of the GameScreen class as the Runnable target, and then you call the start method to get things going.

To comply with the Runnable interface, you also have to add a run method. This is the method called as soon as you call the start method.

private boolean running=true;

public void run()
{
   while(running)
   {
      // do… everything!
   }
}

There's something you can immediately gather from all of this: The fastest the game can go is the maximum CPS. Therefore, the application is now running at the fastest CPS possible. Any code you now add to the game loop will slow this down. Knowing the base CPS at this point in the game and then watching it decrease progressively is an excellent method of tracking the impact of your code. Now add a CPS meter to the application.

Calculating CPS is pretty simple; you just increment a counter every time you go through the game loop. To find out the cycles per second, you also check whether a second has passed, and then record the total. First, you need to add the following tracking fields to the GameScreen class:

private int cps=0;
private int cyclesThisSecond=0;
private long lastCPSTime=0;
You can then use these fields in the game loop. For example:
public void run() 
{
   while(running)
   {
      repaint(); 

      if (System.currentTimeMillis() - lastCPSTime > 1000)
      {
         lastCPSTime = System.currentTimeMillis();
         cps = cyclesThisSecond;
         cyclesThisSecond = 0;
      } else
         cyclesThisSecond++;
   }
}

This code calculates the amount of time that has passed to determine whether a second has elapsed (1000 milliseconds). If it has, you set the lastCPSTime to now (the current time) and mark the CPS rate (since we now know how many cycles occurred up to 1000 milliseconds); otherwise, you increment the number of cycles so far.

I've added a repaint method call so you can display the CPS on the screen. When repaint is called the system will (at some point in the very near future) call the GameScreen paint method to draw everything.

Now you can revise the paint method to draw the number of CPS.

protected void paint(Graphics graphics)
{
   graphics.setColor(0x00000000);
   graphics.fillRect(0, 0, getWidth(), getHeight());
   graphics.setColor(0x00ffffff);
   graphics.drawString("cps=" + cps, 0, 0, Graphics.LEFT
| Graphics.TOP);
}

As you can see from the results in Figure 7.3, your CPS starts out very high! Unfortunately it's just an illusion. As you add features (and start to behave like a proper game), you'll see this number drop significantly.

Figure 7.3. Output from a simple GameScreen showing the starting CPS.

graphic/07fig03.gif


Because you are now rendering a frame on each pass, you can say this is now also your number of FPS (frames per second).

This works nicely, but it's also a bit silly. Do you really need to be cycling this fast? For a start, you'll find your game becomes very jerky when you start to move and animate things. This is because you're hogging the CPU. You need to give the underlying operating system time to do its work as well (such as rendering your graphics). Don't cycle unnecessarily.

To keep things looking smooth, you need to maintain a reasonable frame ratesay 50 per second. If you can achieve this, you should be nice to your operating environment and put your MIDlet to sleep using the Thread.sleep method while doing all your required processing.

For how long should you sleep? There's no need to go overboard and compromise your application's performance because you sent it offto sleep for too long. Therefore, how long you sleep should be relative to the spare time you have. (Sometimes this might be no time at all.) Modify the cycle loop to check whether you have any time left, and then sleep for only that time.

To calculate how much time you have left, you first need to know how much time you allotted to each cycle. If you're under that time at the end of the cycle, you should sleep for the difference. If you set your maximum number of cycles per second to 50, then each cycle has 20 milliseconds to finish. Take a look at the code to do this:

private static final int MAX_CPS = 50;
private static final int MS_PER_FRAME = 1000 / MAX_CPS;
public void run()
{
   while(running)
   {
      // remember the starting time
      long cycleStartTime = System.currentTimeMillis();

      ... process the cycle (repaint etc)

      // sleep if we've finished our work early
      long timeSinceStart = (cycleStartTime - System.currentTimeMillis());
      if (timeSinceStart < MS_PER_FRAME)
      {
         try
         {
            Thread.sleep(MS_PER_FRAME - timeSinceStart);
         }
         catch(java.lang.InterruptedException e) 
         { }
      }
   }
} 

I've made things a little clearer (and more flexible) by using two constants to figure the maximum cycle rate (50) and the subsequent number of milliseconds to which this translates (20). The modified cycle code remembers the cycleStartTime and then sleeps for any leftover time using Thread.sleep(MS_PER_FRAME - timeSinceStart).

NOTE

Tip

In this example the CPS and FPS are effectively the same. In more advanced scenarios you can separate the timing of these, using one rate for redrawing (FPS) and one for other parts of the game (CPS), such as physics and collision detection. If necessary, you can take this even further by allocating processing budgets for different parts of the system depending on their priority, although this is getting a bit heavy-duty for your purposes.

If you run this code now, you'll notice the CPS drops to something near your desired maximum rate. As an exercise, try changing the sleep time to 1. The CPS won't return to your previous super-high levels because the sleep method is quite an expensive call to make.

    Table of ContentsThe Game ScreenAdding the Graphics