Game Object Listeners

You're already familiar with event/listener architectures—an example is AWT's MouseListener interface for receiving mouse events. Because it's an interface, any object can listen in on mouse events. You'll make a similar architecture for game object notifications. First, create the GameObjectEventListener interface, shown here in Listing 14.3.

Listing 14.3 GameObjectEventListener.java
package com.brackeen.javagamebook.scripting;
import com.brackeen.javagamebook.game.GameObject;
/**
 Interface to receive GameObject notification events.
 See GameObject.addListener().
*/
public interface GameObjectEventListener {
 public void notifyVisible(GameObject object, boolean visible);
 public void notifyObjectCollision(GameObject object,
 GameObject otherObject);
 public void notifyObjectTouch(GameObject object,
 GameObject otherObject);
 public void notifyObjectRelease(GameObject object,
 GameObject otherObject);
 public void notifyFloorCollision(GameObject object);
 public void notifyCeilingCollision(GameObject object);
 public void notifyWallCollision(GameObject object);
}


This interface has methods for every type of notification:

  • Object, floor, ceiling, and wall collisions
  • Object touch and release notifications
  • Visible notifications (when an object appears or disappears in the view)

You don't have to limit yourself to these seven notifications, of course. You can always add more later, such as notifyMoving(), notifyStopped(), or notifyDestroyed(), just to give you an idea. Before you adapt your new listeners to the game objects, you should create an easy way for a game object to have multiple listeners. You might need multiple listeners when a game object has regular actions for its class and special unique actions for that game object. You'll make a class that acts as a listener that simply dispatches events to other listeners. This is similar to the AWTEventMulticaster class and is shown in Listing 14.4.

Listing 14.4 GameObjectEventMulticaster.java
package com.brackeen.javagamebook.scripting;
import java.util.*;
import com.brackeen.javagamebook.game.GameObject;
/**
 Adapter to multicast GameObject notifications to multiple
 listeners.
*/
public class GameObjectEventMulticaster
 implements GameObjectEventListener
{
 private List listeners;
 public GameObjectEventMulticaster(GameObjectEventListener l1,
 GameObjectEventListener l2)
 {
 listeners = new LinkedList();
 addListener(l1);
 addListener(l2);
 }
 public void addListener(GameObjectEventListener l) {
 if (l != null) {
 listeners.add(l);
 }
 }
 public void removeListener(GameObjectEventListener l) {
 if (l != null) {
 listeners.remove(l);
 }
 }
 public void notifyObjectCollision(GameObject object,
 GameObject otherObject)
 {
 Iterator i = listeners.iterator();
 while (i.hasNext()) {
 GameObjectEventListener l =
 (GameObjectEventListener)i.next();
 l.notifyObjectCollision(object, otherObject);
 }
 }
 ...
}


This GameObjectEventMulticaster class is itself a GameObjectEventListener, but it just dispatches event notifications to other listeners. Here, only the notifyObjectCollision() method is shown, but you get the idea—it's the same for all other notification methods. Here, the constructor takes two listeners as its parameters because you'll want to multicast to at least two listeners in your code. Okay, the next thing to do is adapt these listeners to work with your existing game objects. First, add get, add, and remove methods to the GameObject class in Listing 14.5.

Listing 14.5 Listener Get/Add/Remove Methods of GameObject.java
private GameObjectEventListener listener;
...
/**
 Gets the GameObjectEventListener for this object.
*/
public GameObjectEventListener getListener() {
 return listener;
}
/**
 Adds a GameObjectEventListener to this object.
*/
public void addListener(GameObjectEventListener l) {
 if (l == null) {
 return;
 }
 else if (listener == null) {
 listener = l;
 }
 else if (listener instanceof GameObjectEventMulticaster) {
 ((GameObjectEventMulticaster)listener).addListener(l);
 }
 else {
 listener = new GameObjectEventMulticaster(listener, l);
 }
}
/**
 Removes a GameObjectEventListener from this object.
*/
public void removeListener(GameObjectEventListener l) {
 if (l == null) {
 return;
 }
 else if (listener == l) {
 listener = null;
 }
 else if (listener instanceof GameObjectEventMulticaster) {
 ((GameObjectEventMulticaster)listener).removeListener(l);
 }
}


This code keeps track of one listener, which can be a multicast listener if more than one listener is registered. With these listeners, the collision-detection and -handling code needs to be changed. The previous way to notify an object was like this:

object.notifyWallCollision();


But using listeners, you use this code:

object.getListener().notifyWallCollision(object);


With that final change, that's all for your new listener architecture. One more thing to do, however, is adapt your existing game objects to easily use the new listener architecture, shown in Listing 14.6.

Listing 14.6 Adapting the New Listener Architecture in GameObject.java
// adapt the listener to older design listener = new GameObjectEventListener() {
 public void notifyVisible(GameObject object,
 boolean visible)
 {
 object.notifyVisible(visible);
 }
 public void notifyObjectCollision(GameObject object,
 GameObject otherObject)
 {
 object.notifyObjectCollision(otherObject);
 }
 public void notifyObjectTouch(GameObject object,
 GameObject otherObject)
 {
 object.notifyObjectTouch(otherObject);
 }
 public void notifyObjectRelease(GameObject object,
 GameObject otherObject)
 {
 object.notifyObjectRelease(otherObject);
 }
 public void notifyFloorCollision(GameObject object) {
 object.notifyFloorCollision();
 }
 public void notifyCeilingCollision(GameObject object) {
 object.notifyCeilingCollision();
 }
 public void notifyWallCollision(GameObject object) {
 object.notifyWallCollision();
 }
};


This code simply forwards the notification to the existing game object methods. This way, subclasses of GameObject (such as Player and PathBot) will still work without any changes. Now you have a listener architecture and triggers. Next, we discuss different ways of scripting actions.



   
Comments