11.4 Colliding walls

One weak point in our standard cCritter::touch and cCritter::collide methods is that they always treat the critters as disks centered on the critter's position. This is only slightly problematic when we have, say, a triangular critter, but it is unworkable if we have a long thin critter such as one might use for a rectangular wall.

In more advanced kinds of game programs, collision detection usually uses a sphere-based collision detection to see if a collision is possible, and then switches to a slower and more detailed algorithm that looks at bounding boxes or individual vertices of the objects being tested for collision. In the Pop Framework we presently only do this for one particular kind of shape: a rectangular 'wall.'

So we have a special cCritterWall class. We normally create our walls with a call to the constructor: cCritterWall::cCritterWall(const cVector &enda, const cVector &endb, Real thickness).

The enda and endb arguments in the constructor are the midpoints of what we consider to be the short ends of the wall (see Figure 11.3).

Figure 11.3. Constructing a cCritterWall


In the Dambuilder game, for instance, we add a bunch of walls in the constructor with lines of the following form. If you don't specify the thickness, the default cCritterWall::THICKNESS of 0.2 is used.

add(new cCritterDamWall( cVector(-4, 2), cVector(3, -2))); 
add(new cCritterDamWall( cVector(1.5, -2), cVector(6, 0.3))); 

If you play with the Dambuilder game, you'll notice you can drag the walls around and you can use the Copy cursor to copy them. You can 'build dams'! Our standard critter's behavior when being dragged is to keep the velocity of the drag; this lets us 'pick up and throw' critters if we like. But we normally don't want walls to drift around, so the cCritter::dragTo is overridden to prevent a wall from acquiring velocity by being dragged.

Ordinarily a critter thinks of its shape as being a disk. If a wall thinks of itself as a disk, then you are prevented from putting two vertical walls near each other, as the imaginary disks would seem to overlap. A similar issue can arise with trying to put a vertical wall near a vertical edge of the screen when the wall's wrap mode is set to cCritter::BOUNCE. Overriding the clamp method partly solves the problem. (We did a quick 'kludge' fix of the BOUNCE problem by overriding cCritterWall::setWrapflag to do nothing, so that in fact you can't put a cCritterWall into the BOUNCE mode. This is unimportant, but we note it here in case you are ever puzzled about why you can't get walls to bounce off the edges of the screen.)

The main effort in coding the cCritterWall went into the override for collide. One needs to consider eight cases; whether a colliding critter is impacting a wall on one of its four corners or on one of its four sides. Another complicating factor is to try and avoid the case where a rapidly moving critter might seem to move 'through' a thin wall by being on one side, and then at the next move step being on the other side, without ever overlapping the wall. We deal with this problem by looking at a critter's 'outcode' relative to the wall; where, as already mentioned in Chapter 8: Critters, an outcode is a flag value reflecting a position relative to a rectangle: inside, to the right, to the right and top, to the top, and so on.

Our cCritterWall::collide(cCritter*pcritter) compares the pcritter 's current outcode relative to the wall to its previous position's outcode relative to this wall, and in this way we can tell whether it jumped over the wall. And if it's hitting the wall, we can tell which region it came from. You can look at the gory details in critterwall.cpp if you're interested.

    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference