GT Computing Home Page

GT Computing Home Page

The Beachball Actor

The controlling script for the beachball looks like this:

//	Ball.java

import java.awt.*;
import linguist.quickshow.*;

public class Ball extends Actor
{
	int base;
	final int accel=1;
	int velocity;
	int top;
	int view=0;
	int offsetX, offsetY;
	int spinRate;

	Ball(Stage stage,int id)
	{
		super(stage,"Ball",id);
	}

	public void run()
	{
		loadImageSequence("gif/D1-Ball","0","7",".gif");
		setCurrentView(0);
		base=stage.getHeight()-getHeight()-10;
		moveAllTo(20+(getID()*100),base);
		show();
		setReady();
		spinRate=100+getID()*2-1;
		postMessage("spin");
		addMouseListener(new ActorAdapter()
		{
			public void mousePressed(Actor a)
			{
				offsetX=getCursorX()-getLeft();
				offsetY=getCursorY()-getTop();
				lock();
			}

			public void mouseReleased(Actor a)
			{
				unlock();
				velocity=0;
				postMessage("drop");
			}
			
			public void mouseDragged(Actor a)
			{
				moveAllTo(getCursorX()-offsetX,getCursorY()-offsetY);
			}
		});
	}

	public Object handleMessage(String command,Object data)
	{
		if (command.equals("spin"))
		{
			view++;
			if (view>=getViews()) view=0;
			setCurrentView(view);
			setTimer(spinRate,"spin");
		}
		else if (command.equals("drop"))
		{
			if (!isLocked())
			{
				velocity+=accel;
				int y=getTop()+velocity;
				if (y>base)
				{
					y=base;
					velocity=1-velocity;
					setScaledHeight(getHeight()/2);
				}
				else setScaledHeight(-1);
				moveAllTo(getLeft(),y);
				if (y!=base || velocity!=0) setTimer(100,"drop");
			}
		}
		return null;
	}
}

Although this is the largest class used by this demo it's still pretty simple. Most of it is concerned with the effect of gravity on a falling object.

The constructor takes a reference to the owning Stage, a name String and an ID. The latter two are used to identify which Actor is which if anyone should ask.

Method run() gets called when the Actor starts up. In here you load any images used by the Actor (some may not need any). In this case the image is eight separate images, loaded as 'views' of a single object. Don't try to use animated GIFs - these give unacceptable screen flashes. The script then computes the position of its baseline and sets up the view to display. Finally it posts itself a message to start spinning. This use of messages is fundamental to the way QuickShow works, allowing the system to multi-task smoothly.

Methods mousePressed(), mouseReleased() and mouseDragged() are called whenever a relevant event occurs. The first locks the Actor to the cursor, the second unlocks it and starts it falling, and the third moves it around the screen. Note how when the ball hits the ground it's momentarily drawn scaled - a rather crude simulation of flattening on impact but a good demonstration of the use of scaling. These three methods are implemented using inner classes and the ActorAdapter utility class. The addMouseListener() method maintains a list inside the Actor of everyone interested in begin notified when a mouse event occurs in the Actor.

Method handleMessage() gets called whenever a message is sent to the Actor. Messages can be simply text strings or they can have a single Object attached, which takes care of just about any eventuality. And you can return a value as an Object if needed. The code in the method examines the command and takes appropriate actions, which in this case means either spinning the ball - by displaying the next view - or computing the position of the ball when dropped. Notice how instead of using a for loop the Actor sends a message to itself. This is much better use of computer processing time than using pauses.

Finally, note that when you move an actor there's no need to 'undraw' it from it's previous position. This is all taken care of automatically by the QuickShow class library.