/// <summary> /// this function will calculate the camera's position and the position of /// its target. From those, we'll update the viewMatrix. /// </summary> private void UpdateCamera() { // The camera's position depends on the tank's facing direction: when the // tank turns, the camera needs to stay behind it. So, we'll calculate a // rotation matrix using the tank's facing direction, and use it to // transform the two offset values that control the camera. Matrix cameraFacingMatrix = Matrix.CreateRotationY(tank.FacingDirection); Vector3 positionOffset = Vector3.Transform(CameraPositionOffset, cameraFacingMatrix); Vector3 targetOffset = Vector3.Transform(CameraTargetOffset, cameraFacingMatrix); // once we've transformed the camera's position offset vector, it's easy to // figure out where we think the camera should be. Vector3 cameraPosition = tank.Position + positionOffset; // We don't want the camera to go beneath the heightmap, so if the camera is // over the terrain, we'll move it up. if (heightMapInfo.IsOnHeightmap(cameraPosition)) { // we don't want the camera to go beneath the terrain's height + // a small offset. float minimumHeight; Vector3 normal; heightMapInfo.GetHeightAndNormal (cameraPosition, out minimumHeight, out normal); minimumHeight += CameraPositionOffset.Y; if (cameraPosition.Y < minimumHeight) { cameraPosition.Y = minimumHeight; } } // next, we need to calculate the point that the camera is aiming it. That's // simple enough - the camera is aiming at the tank, and has to take the // targetOffset into account. Vector3 cameraTarget = tank.Position + targetOffset; // with those values, we'll calculate the viewMatrix. viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up); }
/// <summary> /// This function is called when the game is Updating in response to user input. /// It'll move the tank around the heightmap, and update all of the tank's /// necessary state. /// </summary> public void HandleInput(GamePadState currentGamePadState, KeyboardState currentKeyboardState, HeightMapInfo heightMapInfo) { // First, we want to check to see if the tank should turn. turnAmount will // be an accumulation of all the different possible inputs. float turnAmount = -currentGamePadState.ThumbSticks.Left.X; if (currentKeyboardState.IsKeyDown(Keys.A) || currentKeyboardState.IsKeyDown(Keys.Left) || currentGamePadState.DPad.Left == ButtonState.Pressed) { turnAmount += 1; } if (currentKeyboardState.IsKeyDown(Keys.D) || currentKeyboardState.IsKeyDown(Keys.Right) || currentGamePadState.DPad.Right == ButtonState.Pressed) { turnAmount -= 1; } // clamp the turn amount between -1 and 1, and then use the finished // value to turn the tank. turnAmount = MathHelper.Clamp(turnAmount, -1, 1); facingDirection += turnAmount * TankTurnSpeed; // Next, we want to move the tank forward or back. to do this, // we'll create a Vector3 and modify use the user's input to modify the Z // component, which corresponds to the forward direction. Vector3 movement = Vector3.Zero; movement.Z = -currentGamePadState.ThumbSticks.Left.Y; if (currentKeyboardState.IsKeyDown(Keys.W) || currentKeyboardState.IsKeyDown(Keys.Up) || currentGamePadState.DPad.Up == ButtonState.Pressed) { movement.Z = -1; } if (currentKeyboardState.IsKeyDown(Keys.S) || currentKeyboardState.IsKeyDown(Keys.Down) || currentGamePadState.DPad.Down == ButtonState.Pressed) { movement.Z = 1; } // next, we'll create a rotation matrix from the direction the tank is // facing, and use it to transform the vector. orientation = Matrix.CreateRotationY(FacingDirection); Vector3 velocity = Vector3.Transform(movement, orientation); velocity *= TankVelocity; // Now we know how much the user wants to move. We'll construct a temporary // vector, newPosition, which will represent where the user wants to go. If // that value is on the heightmap, we'll allow the move. Vector3 newPosition = Position + velocity; if (heightMapInfo.IsOnHeightmap(newPosition)) { // now that we know we're on the heightmap, we need to know the correct // height and normal at this position. Vector3 normal; heightMapInfo.GetHeightAndNormal(newPosition, out newPosition.Y, out normal); // As discussed in the doc, we'll use the normal of the heightmap // and our desired forward direction to recalculate our orientation // matrix. It's important to normalize, as well. orientation.Up = normal; orientation.Right = Vector3.Cross(orientation.Forward, orientation.Up); orientation.Right = Vector3.Normalize(orientation.Right); orientation.Forward = Vector3.Cross(orientation.Up, orientation.Right); orientation.Forward = Vector3.Normalize(orientation.Forward); // now we need to roll the tank's wheels "forward." to do this, we'll // calculate how far they have rolled, and from there calculate how much // they must have rotated. float distanceMoved = Vector3.Distance(Position, newPosition); float theta = distanceMoved / TankWheelRadius; int rollDirection = movement.Z > 0 ? 1 : -1; wheelRollMatrix *= Matrix.CreateRotationX(theta * rollDirection); // once we've finished all computations, we can set our position to the // new position that we calculated. position = newPosition; } }