Lesson 6: Threads

Before you start this lesson, you must understand:

  • Anonymous inner class

The simulation logic code

We are going to focus once again at the Game loop code:

        // The main game loop
        while (!finished) {            

            if (env.getKey() == 1)  {
                finished = true;
             }
        
            // Move each fox and tux.
            for (Creature c : creatures) {
                c.move(creatures, dead_creatures);
            }
            
            // Clean up of the dead tuxes.
            for (Creature c : dead_creatures) {
                env.removeObject(c.getEnvObject());
                creatures.remove(c);
            }

            // we clear the ArrayList for the next loop.  We could create a new one 
            // every loop but that would be very inefficient.
            dead_creatures.clear();
        
            // Update display
            env.advanceOneFrame();
        }

To further reduce the code inside our game loop, let's extract the simulation logic code to a new class called Simulator, as follows:

import env3d.Env;
import java.util.ArrayList;

public class Simulator
{
    private ArrayList<Creature> creatures, dead_creatures;    
    private Env env;
    /**
     * Constructor for objects of class Simulator
     */
    public Simulator(Env env, ArrayList<Creature> creatures, ArrayList<Creature> dead_creatures)
    {
        this.env = env;
        this.creatures = creatures;
        this.dead_creatures = dead_creatures;
    }


    public void oneStep()
    {
        // Move each fox and tux.
        for (Creature c : creatures) {
            c.move(creatures, dead_creatures);
        }
        
        // Clean up of the dead tuxes.
        for (Creature c : dead_creatures) {
            env.removeObject(c.getEnvObject());
            creatures.remove(c);
        }

        // we clear the ArrayList for the next loop.  We could create a new one 
        // every loop but that would be very inefficient.
        dead_creatures.clear();        
    }
}

The Simulator class is pretty straight forward. To use it in our Game, we first declare a Simulator object as a field in the Game class. Then, right before the start of the game loop, we create the simulator object and call the oneStep() inside the loop:

        sim = new Simulator(env, creatures, dead_creatures);
        // The main game loop
        while (!finished) {            

            if (env.getKey() == 1)  {
                finished = true;
             }
             
             // Execute one step of the simulation
             sim.oneStep();
             
            // Update display
            env.advanceOneFrame();
        }

What does this gain us? For one, our game loop is now much easier to understand. The benefits of this technique are much more than that. The next section shows just one example of what we can do with the simulator class.

Running the simulator independently

With the code above, each step of our simulation is locked to the frame rate. If the frame rate is fast, our simulation will run faster, if the frame rate is slow, the simulation runs slower. This may not be ideal, as the frame rate is mostly related to the video card speed while the simulation is more dependent on the cpu speed. Locking them together in step means that the video card is limiting the speed of the simulation.

To fix this, we need to run our simulation in parallel to our screen update. This can be accomplished using the Thread class in java:

        sim = new Simulator(env, creatures, dead_creatures);        
        Thread t = new Thread() {
            public void run() {
                while(true) {
                    sim.oneStep();
                    try {
                        sleep(100);
                    } catch (Exception e) {
                    }
                }
            }
        };        
        t.start();
        
        // The main game loop
        while (!finished) {            

            if (env.getKey() == 1)  {
                finished = true;
             }
             
            // Update display
            env.advanceOneFrame();
        }

In the above code, we created an object of the Thread class to perform the simulation loop. When t.start() is called, the thread is started and the Game play() method continues to execute. In effect, there are 2 loops running in parallel, the main game loop and the loop inside the simulation thread. We are currently limiting our simulation thread to update 10 times per second. Experiment by changing the value of the sleep parameter.

Exercises

  1. Try to rewrite the above anonymous inner class Thread declaration to a normal class declaration.