Implementing Touch and Release Notifications

In a game, you often want to perform some action when the player or another object touches another object. For example, a door might open when the player touches a switch, and close when the player touches the switch again. So far in this tutorial's game engine, an object receives all collision events whenever it collides with another object. This means that in this example, as long as the player is touching the switch, the door would just open and close continuously until the player lets go. This isn't the effect you want! In this situation, you're interested only in the first collision event: when the player first touches the switch. In other cases, besides the "touch" notification, you might want the "release" notification, to notify when the player stopped touching an object. See for an example of when the different events occur. In this figure, the player collides with the box on frame 2, sending a touch and collision notification. The player is still moving toward the box in frames 3 and 4, so collision notifications are still sent. In frame 5, the player stops, and no notifications are sent. Finally, in frame 6, the player moves away from the box, so a release notification is sent.

In this example, the player sends a touch notification on frame 2 and a release notification on frame 6. Collision notifications are sent on frames 2, 3, and 4.
In this example, the player sends a touch notification on frame 2 and a release notification on frame 6. Collision notifications are sent on frames 2, 3, and 4.

Keep in mind that there might also be situations where an object could be touching several other objects simultaneously. Okay, now let's get to implementing touch and release notifications in code. Each game object keeps a list of objects that it is touching. When a new object is added to this list, you send a touch notification; when one is removed, you send a release notification. The code for this is in .

Listing 14.1 Touch and Release Code in GameObject.java

private List touching = new ArrayList();
private List touchingThisFrame = new ArrayList();
...
public void notifyObjectCollision(GameObject otherObject) {
 touchingThisFrame.add(otherObject);
}
/**
 After this object has moved and collisions have been checked,
 this method is called to send any touch/release notifications.
*/
public void sendTouchNotifications() {
 // send release notifications
 Iterator i = touching.iterator();
 while (i.hasNext()) {
 GameObject obj = (GameObject)i.next();
 if (!touchingThisFrame.contains(obj)) {
 notifyObjectRelease(this, obj);
 i.remove();
 }
 }
 // send touch notifications
 i = touchingThisFrame.iterator();
 while (i.hasNext()) {
 GameObject obj = (GameObject)i.next();
 if (!touching.contains(obj)) {
 notifyObjectTouch(this, obj);
 touching.add(obj);
 }
 }
 // clean up
 touchingThisFrame.clear();
}

In this code, the additional list called touchingThisFrame is a list of objects that the game object was touching in the last frame. After the objects move and all collisions are detected, the game object manager calls sendTouchNotifications(). This method compares the touchingThisFrame list to the touching list. If an object is in the touching list but not in the touchingThisFrame list, a release notification for that object is sent. Likewise, if an object is in the touchingThisFrame list but not in the touching list, a touch notification for that object is sent, and the object is added to the touching list. Note that sendTouchNotifications() is called only if the object moves. So, only moving objects keep track of what they are touching-the box in doesn't know what it's touching. Likewise, an object that normally moves but isn't moving for a particular frame doesn't send any notifications for that frame, as in frame 5 in .

Triggers

Besides touching and releasing certain objects, sometimes you want certain areas of the map to trigger events. For example, in , an invisible trigger area is used to open and close a door. When the player steps within the trigger area (touches it), the door opens; when the player steps outside it (releases it), the door closes.

In this example, the player interacts with an invisible trigger area to open and close a door.
In this example, the player interacts with an invisible trigger area to open and close a door.

Because you're using cylinders for collision detection here, you can define triggers in map files just like objects, only with a specified radius:

v 512 32 1150
trigger doorTrigger -1 256

This command in the map file creates the game object called doorTrigger at location (512,32,1150) and with a radius of 256. You have to treat triggers as special game objects in only one case: collision detection. You want the player and other objects to walk through triggers, not slide around them as implemented in , "." shows how to avoid sliding for trigger objects but still send the collision notification.

Listing 14.2 Trigger Case in CollisionDetectionWithSliding.java

protected boolean handleObjectCollision(GameObject objectA,
 GameObject objectB, float distSq, float minDistSq,
 Vector3D oldLocation)
{
 objectA.notifyObjectCollision(objectB);
 // if objectB has no polygons, it's a trigger area
 if (objectB.getPolygonGroup().isEmpty()) {
 return false;
 }
 ...
 // (perform sliding)
}

Here, if the object is a trigger area, it has no polygons and you notify the object collision and return from the method as if no collision occurred, without performing any sliding. That's it for triggers. At the moment, however, all these notifications don't do anything; you would have to subclass every object that you want special actions for. Next, you'll create game object listeners that can "listen in" on notifications for certain objects and perform actions based on those notifications.