/// <summary> /// Load your graphics content. /// </summary> protected override void LoadContent() { terrain = Content.Load <Model>("terrain"); // The terrain processor attached a HeightMapInfo to the terrain model's // Tag. We'll save that to a member variable now, and use it to // calculate the terrain's heights later. heightMapInfo = terrain.Tag as HeightMapInfo; if (heightMapInfo == null) { string message = "The terrain model did not have a HeightMapInfo " + "object attached. Are you sure you are using the " + "TerrainProcessor?"; throw new InvalidOperationException(message); } tank.LoadContent(Content); }
/// <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; } }