ActiveObject

The fact that objectdraw programs are event-driven also means that they're very reactionary; they do things only in response to mouse and keyboard input. Sometimes this won't be enough for you; you'll want your program to actually do something continuously, without input. A typical example would be some sort of animation.

The ActiveObject class is a special class that is a Java thread - like a separate program running at the same time as the first. As long as most of the threads spend a lot of time asleep - pausing or waiting for an event - the computer can easily handle hundreds of thread at once, each of them running at their own location in the program.

Table of Contents

1. Writing an ActiveObject class

Your browser doesn't do Java.
BounceRunner.java
BouncingBall.java

The simplest way to make a separate thread is to write a class that extends ActiveObject. This will allow you to use the pause() methods that are part of the ActiveObject class. Then the only other thing that you need to do is to write a run() method, which is what will actually be done when code elsewhere tells your ActiveObject to start().

public class ClassName extends ActiveObject {
    public void run() {
        doSomething();
        pause(1000);
        doSomethingElse();
        pause(1000);
        doSomeFinalThing()
    }
}

This sample ActiveObject will do three things with a one second pause in between each. The pause() method measures time in milliseconds - thousandths of a second - so that 1000 means one second.

You will probably also want to write a constructor for your class. Typically your constructor will take a DrawingCanvas as a parameter and use it to create objects; the run() method will then move those objects a little bit at a time, often with a loop, pausing after each incremental move. So, for example, this ActiveObject class makes a rectangle filling the screen and then moves it upwards:

public class StageCurtain extends ActiveObject {
    FilledRect rect;
    public StageCurtain)DrawingCanvas canvas) {
        rect = new FilledRect(0, 0, canvas.getWidth(), canvas.getHeight(), Color.red, canvas);
    }
    public void run() {
        while(rect.getY() > -rect.getCanvas().getHeight()) {
            rect.move(0, -5);
            pause(50);
        }
    }

Your browser doesn't do Java.
ActiveTurtleRunner.java
ActiveMeshTurtle.java
The ActiveObject need not merely have a single loop in its run() method; you may want to have multiple loops or call other methods. As long as you have it pause() briefly after each step of animation, it will appear to move things step by step. The ActiveMeshTurtle class in the example to the right has the turtle pause after every step in a long pattern of drawing so that you can see what it is doing.

2. Creating and running your ActiveObject

Because you defined your own constructor for ActiveObject, you have control over what parameters it needs when you create it. In the case of the StageCurtain I defined above, the only parameter it needs is a DrawingCanvas. So, I would create it from my WindowController like this:

StageCurtain curtain = new StageCurtain(canvas);

Once the ActiveObject is created, you need to start() it in order for its run() method to start going. You might do this immediately upon creatign it, or wait until some later event:

StageCurtain curtain = new StageCurtain(canvas);
...
curtain.start();

// Or, start it as soon as it is created:
new StageCurtain(canvas).start();

The start() method starts up a new thread to run the ActiveObject, and returns immediately. So, after you call start() the code after that will run right away, not waiting for the run() method to finish as often happens with methods.

If the ActiveObject is already started, there will be an exception thrown if you try to start it again. So, for example, if I am waiting for the user to click before starting, I usually have a boolean variable to remember whether I have already started, so that I won't do it more than once.

3. ActiveObjects can create other ActiveObjects

Your browser doesn't do Java.
SunflowerPatch.java
Sunflower.java
Seed.java
Often you will want to have some kind of ActiveObject be created over and over as time goes on. The example to the right is a simulation of how the seeds of a sunflower grow, a classic demonstration of why the golden ratio shows up often in nature. Every Seed is an ActiveObject, moving gradually from the center to the outside and then disappearing. In order to create a Seed twenty times a second, I created another ActiveObject class, called Sunflower, that just sits at the middle making a new Seed over and over again.

You will find that this pattern is often useful, having one ActiveObject whose role is to create and start more ActiveObjects as time goes on.

Incidentally, you should note that the Sunflower has about 600 Seeds moving at once, and yet runs on most computers without any lag. As long as each thread spends most of its time sleeping - the Seed thread does a single moveTo(), then sleeps for 50 milliseconds - it doesn't matter much how many of them there are.

4. You can also run threads without an ActiveObject

Your browser doesn't do Java.
ThermalRunner.java
ThermalBall.java
Occasionally you will want to create an object that runs in a separate thread, but you will not be able to make it extend ActiveObject. The most common reason for this would be that you want your new class to extend one of the drawable classes so that it itself will be on the canvas and will be accessible with getDrawableIterator().

You can see an example of this to the right. This is a simulation of the motion of molecules in a gas. As they collide with each other, the rules of elastic collisions in physics result in their ending speeds being somewhat unequal; this is how heat (kinetic energy of individual molecules) ends up being passed around, and is also the reason why there are always some molecules that are a lot faster than average and some that are a lot slower.

Because every molecule needs to be able to collide with any other molecule, I had to have a list of them that I could access somehow. The simplest way to do this was to make the molecule a subclass of FilledOval, so that I could add it to the canvas as an oval. I can then look through all the objects on the canvas with getDrawableIterator(), and react somehow to those that are other ThermalBalls.

To make it possible for an object that is not an ActiveObject to be run as a thread, all you need is to give it a run() method, and inform the computer that you have done this by declaring the class to implement an interface called Runnable:

public class ThermalBall extends FilledOval implements Runnable {
    ...
    public void run() {
        ...
    }
}

I can then run this object by creating a ActiveObject with it as a target, and telling that ActiveObject to start():

new ActiveObject(new ThermalBall()).start()

5. ActiveObjects controlled by mouse or keys

Another very natural thing to want to do with an ActiveObject is to have it be controlled by the mouse or keys. You can use the same mouse and key event methods that you are accustomed to having in WindowController if you use the interpreter classes provided for these events in objectdraw. You can read more about this in KeyInterpreter and MouseInterpreter.