13.5 Armed players and armed robots

In this section we discuss the cCritterArmedPlayer and cCritterArmedRobot child classes of cCritterArmed.

The cCritterArmedPlayer objects shoot when the user presses the spacebar or left-clicks the mouse. We implement this by deriving our players from the cCritterArmedPlayer class and letting cCritterArmedPlayer override the feellistener(Real dt) method so as use the left button or the spacebar to fire its gun. That is, as we already mentioned in Chapter 12: Listeners, we'll have an override something like this.

void cCritterArmedPlayer::feellistener(Real dt) 
    _bshooting = (pgame()->keystate(VK_SPACE) == cController::KEYON); 
    if (pgame()->keystate(VK_LBUTTON) == cController::KEYON) 
        /* shoot with left mouse click. The controller will only have 
        turned VK_LBUTTON on if you left clicked the Shoot Cursor; 
        left clicks with other cursors will be ignored by 
        controller. */ 
        _bshooting = TRUE; 

The cCritterArmedRobot objects, on the other hand, have their _bshooting flag permanently set to TRUE, so they shoot as often as they can, that is, whenever _waitshoot seconds have elapsed. To prevent a boring 'firing-squad effect' of having all the armed robots shoot in synchronization, we put a little age-randomizing trick into an override of the cCritterArmedRobot::setWaitShoot(Real waitshoot) method; you can check this and other details in critterarmed.cpp.

As we mentioned above, a bullet has a muzzle velocity given by the shooter's aim direction and the bullet's maximum speed. We normally take the bullet's velocity to simply be the muzzle velocity, without adding in the shooter's velocity. This is a good idea, in particular, for the moving cCritterArmedRobot critters, as we want to make it easy for them to aim and shoot directly at the player without having to worry about computing 'windage' compensation for their velocity relative to the player.

When it comes to the player, however, it makes the game feel a little more realistic if we do go at least part way towards adding the player velocity to the bullet muzzle velocity. Otherwise when you're moving forward and shooting, the bullets seem to pile up on each other. And, given that the player is controlled by a thinking human being instead of by two or three lines of computer code, it might be perfectly alright if the player's aiming process is made slightly more difficult.

But experimentation and beta-testing shows that directly adding the player velocity to the muzzle velocity gives effects that confuse some users. Remember that we aren't necessarily after complete physical accuracy here; the bottom line is to make an enjoyable game with controls that feel natural. So we actually only add in whatever component of the player's motion is in the direction the bullet is already moving. Provided that the bullet's default speed is less than its maximum allowable speed, this means that when a moving player shoots a bullet straight ahead it goes a little faster. We don't reciprocate by slowing down a bullet that the player shoots opposite to its direction of motion as this could lead to the visually confusing phenomenon of very slowly moving bullets.

cCritterBullet* cCritterArmedPlayer::shoot() 
    cCritterBullet *pbullet = cCritterArmed::shoot(); 
    /* A simple pbullet->addVelocity(_velocity), gives unattractive 
        results. Use dot product operator % */ 
    Real bulletspeedup = _velocity % pbullet->tangent(); 
    if (bulletspeedup > 0.0) 
        pbullet->addVelocity(bulletspeedup * pbullet->tangent()); 
            //So bullets don't stack up. 
    return pbullet; 

Some of the additional features of the cCritterArmedPlayer class are as follows.

  • The constructor calls setAttitudeToMotionLock(FALSE) so the player can turn its sprite independently of its motion, and calls setAimToAttitudeLock(TRUE) so the player will align its gun with the attitude of its sprite.

  • The constructor installs a default red isosceles triangle as the armed player's sprite.

  • The class overrides damage to make a noise so the user knows something bad has happened.

  • The class overrides draw to draw a circle around the player's sprite; this is to make it stand out from the other critters.

  • The class adds a BOOL _sensitive field and overrides collide so that if _sensitive is TRUE, the armed player calls damage each time it touches another non-wall critter. This is useful for an Asteroids-style game. Since cCritterArmedPlayer has its own override of collide, the constructor sets its _collidepriority to cCollider::CP_PLAYER which is slightly higher than cCollider::CP_CRITTER, but less than cCollider::CP_BULLET.

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