IMoveableObject Indeed


IMoveableObject Indeed

Have you noticed that you haven't implemented the IMoveableObject members yet? If you've tried to compile, you have because you can't compile the class until you do. Listing 14.9 contains the implementation of these members.

Listing 14.9. IMoveableObject Implementation
 /// <summary> /// Draw the player (the tank) in the world /// </summary> /// <param name="device">Device used to render tank</param> public void Draw(GameEngine engine, Camera c) {     if (gameTank != null)     {         // Only draw the tank if it lies in the view frustum         if (c.ObjectInFrustum(this))         {             gameTank.Draw(engine);         }     } } /// <summary> /// The tank's current position /// </summary> public Vector3 Position {     get { return gameTank.Position; }     set { gameTank.Position = value; } } /// <summary> /// The direction the player is currently facing /// </summary> public Vector3 Direction {     get { return gameTank.TankDirection; } } /// <summary> /// The current rotation of the tank /// </summary> public float Rotation {     get { return gameTank.TankRotation; }     set { gameTank.TankRotation = value; } } /// <summary> /// Radius of the player model /// </summary> public float Radius {     get { return gameTank.Radius; } } 

There is still one method left to implement in the interface (the Update method), but that method is quite large and I discuss it separately. The rest of the implementation is straightforward. When rendering, you check whether the player is within the view frustum, and if he is, you call the draw method on the tank. The properties simply return the associated tank property you've already defined. Now it's time to look at the big one, which is in Listing 14.10.

Listing 14.10. Updating the Player
 public void Update(float elapsedTime, Level currentLevel) {     System.Diagnostics.Debug.Assert(gameTank != null, "Tank object cannot                                                         be null.");     // Keep track of the total time     totalTime += elapsedTime;     // Update movement if the player is local     if (isPlayerLocal)     {         // If the joystick can be used, try to use it         if (joystickDevice != null)         {             UpdateJoystick(joystickDevice);         }         // Is the player moving?         if ((isMovingForward) || (isMovingBackward) || (isMovingForwardJoy)              || (isMovingBackwardJoy))        {             // Movement is happening, first get the direction the tank is facing             Vector3 dir = gameTank.TankDirection;            // Now scale it to move, first check keyboard            if ((isMovingForward) || (isMovingBackward))            {                dir.Scale((isMovingForward) ? (elapsedTime *                MaximumMovementSpeed) : (elapsedTime * -MaximumMovementSpeed));            }            else // Must be joystick            {                dir.Scale((isMovingForwardJoy) ? (elapsedTime *                MaximumMovementSpeed) : (elapsedTime * -MaximumMovementSpeed));            }            // Now append that to the current position            gameTank.Position += dir;            Plane reflectionPlane;            if (currentLevel.HitTest(this, out reflectionPlane))            {                // Well, you're hitting a wall. Back up                gameTank.Position -= dir;                // Now stop moving                isMovingBackward = false;                isMovingForward = false;            }            // Now update the wheel rotation animation            gameTank.WheelRotation += (isMovingForward) ? (elapsedTime *                 -MaximumMovementSpeedWheels) :                (elapsedTime * MaximumMovementSpeedWheels);            }            // Is the player rotating?            if ((isRotatingLeft) || (isRotatingRight) || (isRotatingLeftJoy)                || (isRotatingRightJoy))            {                // First check keyboard                if ((isRotatingLeft) || (isRotatingRight))                {                   gameTank.TankRotation += (isRotatingRight) ? (                         MaximumRotationSpeed * elapsedTime) :                       (-MaximumRotationSpeed * elapsedTime);                }                else // Must be joystick                {                   gameTank.TankRotation += (isRotatingRightJoy) ? (                         MaximumRotationSpeed * elapsedTime) :                       (-MaximumRotationSpeed * elapsedTime);                }            }     }     // Finally update the tank     gameTank.Update(elapsedTime); } 

Because you need to know the total time when determining whether it's "safe" to fire, you constantly increment the total time based on the elapsed time since the last update. Next, you need to check whether the player you're updating is a local player, and if he is, you need to find out whether you need to move the player. Assuming the player is local and a joystick is attached, first you check the joystick data (see Listing 14.11).

Next, you detect whether you should be moving either forward or back (by either the joystick or the keyboard). If you are moving, the direction you are facing is stored first and then scaled by the movement speed of the tank (or a fraction of that because you're basing it on the elapsed time since the last frame). Notice that the scaling only happens once, regardless of whether you're using both the joystick and the keyboard, and that the scaling is "negated" (reversed) if you are moving backwards. You then update the position of the tank by adding the newly scaled direction to the current position.

The next call is to a HitTest method on the level object, which you certainly didn't implement yet. You do that shortly (see Listing 14.12), but the goal of the method is to see whether the object (in this case, the tank) has hit an obstacle in the level. If you hit an obstacle, you return to your original position and stop moving because you shouldn't be allowed to move through the obstacle. Rotating is much like the movement, but there is no check for hitting an obstacle. Finally, you call the Update method on the tank itself; it correctly sets up the frame matrices and the orientation of the tank based on the rotation.

Listing 14.11. Updating the Player
 private void UpdateJoystick(Input.Device joystick) {     try     {         // See if we can get the joystick data         joystick.Poll();     }     catch(Input.InputException e)     {         if ((e is Input.NotAcquiredException) || (e is Input.InputLostException))         {             // If the joystick wasn't acquired, or was lost,             // try to acquire it now             try             {                 // Acquire the device                 joystick.Acquire();             }             catch(Input.InputException)             {                 // Failed to acquire the device, nothing we can do, exit                 return;             }         }     }     // Get the state of the device.     Input.JoystickState state = joystick.CurrentJoystickState;     // Now update the player     if (state.X > 0) { isRotatingRightJoy = true; isRotatingLeftJoy = false; }     else if (state.X < 0) { isRotatingLeftJoy = true;                            isRotatingRightJoy = false;}     else { isRotatingLeftJoy = false; isRotatingRightJoy = false;}     if (state.Y < 0) { isMovingForwardJoy = true;                       isMovingBackwardJoy = false; }     else if (state.Y > 0) { isMovingBackwardJoy = true;                            isMovingForwardJoy = false;}     else {isMovingBackwardJoy = false; isMovingForwardJoy = false; }     // See if the first button has been fired     byte[] buttons = state.GetButtons();     if ((buttons[0] & 0x80) != 0)     {         // Fire!         RaiseFireEvent();     }     // Check the hat     if (hasHat)     {         // Update the gun barrel based on POV         int[] pov = state.GetPointOfView();         switch(pov[0])         {            case 0:                // Move barrel up                gameTank.GunBarrelAngle -= (MaximumGunSpeed);                break;            case 4500:                // Move barrel up, and turret over                gameTank.GunBarrelAngle -= (MaximumGunSpeed);                gameTank.GunTurretAngle += (MaximumGunSpeed);                break;            case 9000:                // Move turret over                gameTank.GunTurretAngle += (MaximumGunSpeed);                break;            case 13500:                // Move barrel down, and turret over                gameTank.GunBarrelAngle += (MaximumGunSpeed);                gameTank.GunTurretAngle += (MaximumGunSpeed);                break;            case 18000:                // Move barrel down                gameTank.GunBarrelAngle += (MaximumGunSpeed);                break;            case 22500:                // Move barrel down, and turret over                gameTank.GunBarrelAngle += (MaximumGunSpeed);                gameTank.GunTurretAngle -= (MaximumGunSpeed);                break;            case 27000:                // Move turret over                gameTank.GunTurretAngle -= (MaximumGunSpeed);                break;            case 31500:                // Move barrel up, and turret over                gameTank.GunBarrelAngle -= (MaximumGunSpeed);                gameTank.GunTurretAngle -= (MaximumGunSpeed);                break;         }     } } 

This method looks longer and more difficult than it really is. The first call is intended to poll the device (your joystick) for the latest data. However, if you've lost the device for some reason (such as another application taking exclusive access), the call will fail. If it fails because the device isn't acquired or it has been lost, try to acquire it, and if that fails, well, there's nothing else you can do, so simply leave the method. It will be tried again next frame.

If the device was polled successfully, though, you get the current joystick state because it will have the current data in it. You can then check the x axis, see whether it's moving right or left, and set the appropriate variable (or set them both to false if you aren't moving on the x axis). You do the same thing for the y axis. If the first joystick button is pressed, you call the RaiseFireEvent method, just as you do when the user clicks the mouse button that handles the firing and gun cool-down.

Finally, you check the device is checked for a hat, and if it has one, you use its state to determine which way to move the gun turret and barrel. Notice the pattern here: the position of the hat would be equivalent to the angle of the hat (in degrees) multiplied 100, so the possible values go from 0 to 36,000. A value of -1 means the hat is in its default position.



Beginning 3D Game Programming
Beginning 3D Game Programming
ISBN: 0672326612
EAN: 2147483647
Year: 2003
Pages: 191
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net