EventTimer

The EventTimer class designed to be a simpler alternative to ActiveObject. Instead of creating an entire ActiveObject subclass and giving it a run() method that does some action repeatedly, you can simply create an EventTimer and tell it to call a particular method of a particular object repeatedly, at regular intervals. This makes it possible to write complex, real-time games and animations in a simple, one-class program such as the "snake" game below.
Your browser doesn't do Java.
Snake.java

Table of Contents

1. Creating and starting an EventTimer

Your browser doesn't do Java.
Tetris.java
Suppose that when the user clicks, you want a spot to appear where they clicked, but two seconds later you want it to disappear. In other words, you want to make a new FilledOval(), and then call its removeFromCanvas() method after two seconds.

An EventTimer object is what you need to do this. Your onMousePress() method might look like this:

public void onMousePress(Location point) {
    point.translate(-10, -10);
    FilledOval oval = new FilledOval(point, 20, 20, canvas);
    EventTimer timer = new EventTimer(oval, "removeFromCanvas");
    timer.start(2, 0);

Here, the constructor of EventTimer takes two arguments: the object whose method should be called, and the name of the method. When the timer fires, the result will be exactly as if you had called oval.removeFromCanvas().

The start() method takes two parameters: how long to wait before the first time the timer fires, and how long to wait between firings after that. Here, by passing a value of 0 for the delay, I tell this timer to fire only once and then stop.

2. Passing parameters to the target method

Your browser doesn't do Java.
TimerDemo.java
The example above was simplified by the fact that the method I called had no parameters. It is also possible to call a method with parameters. In this case, you should list the values to pass for those parameters after the method name in the constructor.

If the method you are calling requires parameters of a primitive type - int, double, and so on - you may have to "wrap" that value in the appropriate wrapper class, such as Integer or Double. This is only necessary if you don't have Java 1.5 or higher. Nearly everyone has Java 1.5 these days, but ObjectDraw is supposed to be compatible down to 1.4, so this is how to get around that if you are still using 1.4.

So, for example, if I wanted the spots created in the previous example to wait two seconds, then fall downward, I could do this:

EventTimer timer = new EventTimer(oval, "move", new Double(0), new Double(1));
timer.start(2, .02);

This code says, "Wait two seconds, then call oval.move(0, 1) every .02 seconds." On a modern version of Java, I could just do new EventTimer(oval, "move", 0, 1).

Often, the timer action will be complex enough that, instead of calling an existing method of your object, you will want to create a method of your own and call that instead. You just have to make sure that the method is public. For example, suppose that I want to remove oval from the canvas once it has moved past the bottom of the window. I can then create a method that will move oval and then check its position, and then have the EventTimer call that method:

public void onMousePress(Location point) {
    point.translate(-10, -10);
    FilledOval oval = new FilledOval(point, 20, 20, canvas);
    new EventTimer(this, "moveDown", oval, EventTimer.SELF).start(2, .02);
}

public void moveDown(FilledOval oval, EventTimer timer) {
    oval.move(0, 1);
    if(oval.getY() > canvas.getHeight()) {
        oval.removeFromCanvas();
        timer.stop();
    }
}

In this example you see also another detail of how passing parameters works. If I want to have the timer itself be passed as a parameter to the target method, I must use the special value EventTimer.SELF as a placeholder for that parameter, since there is no way that I can have a reference to the EventTimer object before I have constructed it.

3. Passing four or more parameters

There are constructors that allow you to pass zero, one, two, or three parameters to the target method by simply listing them as objects. If you want to pass more parameters than that, you will have to use this constructor instead:

public EventTimer(Object target, String methodName, Object[] args)

Because it takes an array of objects, this timer can pass as many parameters as you like. So, for instance, the example at the top of this page creates a timer that calls a method with four parameters:

new EventTimer(this, "moveSpot", new Object[]{
            spot, new Double(Math.random() * 2 - 1), new Double(Math.random() * 2 - 1), EventTimer.SELF
}).start(2, .01);

4. Checking the exact time between firings

Your browser doesn't do Java.
Droplets.java
The computer will do its best to fire the timer on the schedule that you set, but if it is really busy, it might not be able to keep up. The applet shown to the right is an example of this. Every spreading-out ripple is controlled by an EventTimer that is supposed to fire every .05 seconds. Since there can easily be hundreds of ripples on the screen at once, that means thousands of firings per second, which is more than the computer can handle.

In a case like this, if I was moving each ripple by a fixed amount each firing (say, expanding each edge by 1 pixel) then it would be very obvious that the ripples had slowed down when there were too many of them on the screen. A much better behavior, which you see here, is to just have the ripples spread out further if the time since the last firing is more than the specified .05 seconds. In this particular case, the amount by which the ripple spreads out is 20 times the time since last firing; this means that it would be 1 pixel per frame if the timer were firing at the nominal rate.

There are methods in EventTimer to find the time since the last firing, the initial firing, or when the timer was started:

public double getTimeSinceStart()
public double getTimeSinceInitialFiring()
public double getTimeSincePreviousFiring()

So, for example, in the "droplets" demo, the color of each ripple is set based on the time since the initial firing. This means that all ripples start out red and progress through the rainbow as time goes by.

In the "droplets" demo, I also wanted to make each droplet start out hidden, and appear only upon the initial firing of its timer. To accomplish that, I used this method of EventTimer to check if a given firing of the timer was the first one:

public boolean isInitialFiring()

Since the methods described in this section require access to the EventTimer while an event is being processed, you will probably want to use a SELF parameter in your target method, as described above.

5. Other methods to manipulate an EventTimer

You may want to start and stop a timer, or find out if it is running. In that case, you will find these methods useful:

public void start()
public void stop()
public void isRunning()

The start() method simply restarts the timer, without changing its delays. This will make the timer wait again for its initial delay before continuing to fire. If you have not set any delays for this timer at all, it will use the default value of one second.

There are methods to get and set the delay and start delay of an EventTimer, without restarting it:

public double getStartDelay()
public double getDelay()
public void setDelay(double startDelay, double delay)

If you call setDelay() on an EventTimer that is already running, it will try its best to make its next firing be delay seconds from when it last fired. So, for example, if I start a timer with a delay of 100 seconds, but later setDelay() that to five seconds, it will fire immediately if it has been more than five seconds since it last fired, and otherwise it will schedule a new firing for five seconds after it last fired.

You may occasionally want to be able to fire an EventTimer instantly, without running it. If so, you can call this method:

public void fire()

As an example of how this would be used: Suppose that your program asks the user to hit a key from "1" to "9" and responds differently to each key. You could write a separate on1Press() method for each key, or you could do something like this:

public void onKeyTyped(String key) {
    int key = key.charAt(0) - '0';
    if(key >= 0 && key <= 9) responses[key].fire();
}

Here, responses would be an array of EventTimer objects, representing what method ought to be called when each of the nine number keys was pressed.

6. Other useful information

It is not possible to have EventTimer call a constructor. If you find yourself wanting to do this, just have it call a method of this and write that method to construct the object desired.

It is possible to have EventTimer call a static method. If you want to do this, pass along the class object for the class containing that method. For example:

new EventTimer(Math.class, "sin", new Double(Math.PI)).start();

This code will call Math.sin(Math.PI) every second. You may also pass along as a target an object of the class whose static method you want to call; EventTimer will still be able to figure out that the method is static. Passing the class is allowed for convenience, because you may not have access to an instance of the class whose static method you want to call.

It is possible to change the target, method, and arguments of a timer while it is running. The setTarget() method takes the same set of arguments as the constructor, and this change will be immediately applied to any future firings. Often, however, it makes more sense to have instance variables storing the values of any parameters you would like to change.

Alternately, if one of the parameters you pass to the target method is an array, or an object whose instance variables you can manipulate, changes you make in one call to that method will be visible the next time it is called.

If the method you tell an EventTimer to run does not exist, is not public, or cannot be called with the list of parameters you provided, you will get an IllegalArgumentException as soon as you try to create the timer.

7. Complete listing of EventTimer methods

Creating an EventTimer:

public EventTimer(Object target, String methodName)
public EventTimer(Object target, String methodName, Object arg1)
public EventTimer(Object target, String methodName, Object arg1, Object arg2)
public EventTimer(Object target, String methodName, Object arg1, Object arg2, Object arg3)
public EventTimer(Object target, String methodName, Object[] args)

Changing the target, method, or parameters:

public void setTarget(Object target, String methodName)
public void setTarget((Object target, String methodName, Object arg1)
public void setTarget((Object target, String methodName, Object arg1, Object arg2)
public void setTarget((Object target, String methodName, Object arg1, Object arg2, Object arg3)
public void setTarget((Object target, String methodName, Object[] args)

Getting information about the actual elapsed time:

public double getTimeSinceStart()
public double getTimeSinceInitialFiring()
public double getTimeSincePreviousFiring()

Determining if this is the first firing:

public boolean isInitialFiring()

Starting, stopping, and checking if it is running:

public void start()
public void start(double startDelay, double delay)
public void stop()
public void isRunning()

Getting and setting the delays without interrupting the timer:

public double getDelay()
public double getStartDelay()
public void setTimer(double startDelay, double delay)

Javadoc for EventTimer.