Microsoft XNA Game Studio Creator’s Guide- P16 ppt

30 308 0
Microsoft XNA Game Studio Creator’s Guide- P16 ppt

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

These next instructions belong inside LoadContent() to load the spaceship model: shipModel = Content.Load<Model>("Models\\alien1"); shipMatrix = new Matrix[shipModel.Bones.Count]; shipModel.CopyAbsoluteBoneTransformsTo(shipMatrix); UpdateShipPosition() is used in the game class to not only move the ship on X and Z but also Y to match the height of the terrain below: void UpdateShipPosition(GameTime gameTime){ const float HOVER_DISTANCE = 0.04f; // ship's X, Y, Z position without hover distance above the ground shipPosition.Y = shipPosition.Y - HOVER_DISTANCE; // reverse direction if right boundary exceeded if (shipPosition.Z < -BOUNDARY && positiveDirection == false){ shipVelocity *= -1.0f; positiveDirection = true; } // reverse direction if left boundary exceeded else if (shipPosition.Z > BOUNDARY && positiveDirection == true){ shipVelocity *= -1.0f; positiveDirection = false; } // increment position by time scale so speed is same on all systems float time = (float)gameTime.ElapsedGameTime.Milliseconds/200.0f; shipPosition.Z+= shipVelocity.Z * time; shipPosition.X+= shipVelocity.X * time; shipPosition.Y = CellHeight(shipPosition) + HOVER_DISTANCE; } To update the ship height each frame, trigger the ship update at the end of the Up- date() method: UpdateShipPosition(gameTime); When drawing objects that use the terrain, you need to do more than just update their positions and directions about the Y axis. You also need to update their orienta- tion relative to the slope of the terrain where they are located. This next section of code allows you to do this. MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 428 429 When you’re drawing the spaceship, the ship’s Up vector is calculated using a weighted average of leading and trailing normal vectors in the ship’s path (see Figure 25-5). This weighted average prevents a jerking motion caused as the ship’s Up vector changes from one cell to the next. If you want to make it look as if you don’t have any shock absorption, you can just use the normal vector for the current cell only. Whether you are calculating weighted or normal vectors, a method is required to project or interpolate the object’s position ahead or behind. When directionScalar equals +1, the position in one cell ahead is determined. When directionScalar equals -1, the position one cell behind is determined. Add ProjectedXZ() to the game class to interpolate the X and Z positions for objects in leading and trailing cells: public Vector3 ProjectedXZ(Vector3 position, Vector3 speed, float directionScalar){ // only consider change in X and Z when projecting position // in neighboring cell. Vector3 velocity = new Vector3(speed.X, 0.0f, speed.Z); velocity = Vector3.Normalize(velocity); float changeX = directionScalar * terrain.cellWidth * velocity.X; float changeZ = directionScalar * terrain.cellHeight * velocity.Z; return new Vector3(position.X + changeX, 0.0f, position.Z + changeZ); } CellWeight() determines the remaining distance within the current cell relative to the total distance projected into the neighboring cell. This fraction is then used to CHAPTER 25 Terrain with Height Detection FIGURE 25-5 Trailing and loading normal vectors weight the height values and Up vectors in trailing and leading height map cells. CellWeight() belongs in the game class: float CellWeight(Vector3 currentPosition, Vector3 nextPosition){ Vector3 currRowColumn = RowColumn(currentPosition); int currRow = (int)currRowColumn.Z; int currCol = (int)currRowColumn.X; Vector3 nextRowColumn = RowColumn(nextPosition); int nextRow = (int)nextRowColumn.Z; int nextCol = (int)nextRowColumn.X; // find row and column between current cell and neighbor cell int rowBorder, colBorder; if (currRow < nextRow) rowBorder = currRow + 1; else rowBorder = currRow; if (currCol < nextCol) // next cell at right of current cell colBorder = currCol + 1; else colBorder = currCol; // next cell at left of current cell Vector3 intersect = Vector3.Zero; // margins between current // and next cell intersect.X = -BOUNDARY + colBorder*terrain.cellWidth; intersect.Z = -BOUNDARY + rowBorder*terrain.cellHeight; currentPosition.Y = 0.0f; // not concerned about height // find distance between current position and cell border Vector3 difference = intersect - currentPosition; float lengthToBorder = difference.Length(); // find distance to projected location in neighboring cell difference = nextPosition - currentPosition; float lengthToNewCell = difference.Length(); if(lengthToNewCell==0) // prevent divide by zero return 0.0f; // weighted distance in current cell relative to the entire MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 430 431 // distance to projected position return lengthToBorder / lengthToNewCell; } Since the normal vector is projected in the cell ahead or trailing cell behind, an ad- justment is required to handle situations where the current and projected cell are both off the height map. Replace the existing HandleOffHeightMap() method with this revision to remedy this case. If you don’t, you will notice the spaceship dis- appears when it reaches the end of the world when Z is positive: private void HandleOffHeightMap(ref int row, ref int col){ if (row >= terrain.NUM_ROWS) row = terrain.NUM_ROWS - 2; else if (row < 0) row = 0; if (col >= terrain.NUM_COLS) col = terrain.NUM_COLS - 2; else if (col < 0) col = 0; } CellNormal() receives the height map row and column as parameters and re- turns the corresponding normal vector. The normal vector serves as a measure of up- rightness for the object travelling above this location: Vector3 CellNormal(int row, int col){ HandleOffHeightMap(ref row, ref col); return terrain.normal[col + row * terrain.NUM_COLS]; } Normal() projects the normal vector inside the cell according to the position rel- ative to the surrounding height map cell vertices. Chapter 24 explains the Lerp() calculation behind this projection: Vector3 Normal(Vector3 position){ // coordinates for top left of cell Vector3 cellPosition = RowColumn(position); int row = (int)cellPosition.Z; int col = (int)cellPosition.X; // distance from top left of cell float distanceFromLeft = position.X%terrain.cellWidth; CHAPTER 25 Terrain with Height Detection float distanceFromTop = position.Z%terrain.cellHeight; // use lerp to interpolate normal at point within cell Vector3 topNormal = Vector3.Lerp( CellNormal(row, col), CellNormal(row,col+1), distanceFromLeft); Vector3 bottomNormal = Vector3.Lerp( CellNormal(row+1,col),CellNormal(row+1,col+1),distanceFromLeft); Vector3 normal = Vector3.Lerp( topNormal, bottomNormal, distanceFromTop); normal.Normalize(); // convert to unit vector for consistency return normal; } NormalWeight() is needed in the game class to allocate a weighted portion for each normal vector contained in a fixed range along the object’s path, as shown in Figure 25-5. These weighted normal vectors are later combined to generate the up- right vector for the spaceship. If you only use the current normal vector for your ship’s Up direction, you will notice sudden changes in orientation at each cell and the ride will appear to be a rough one: Vector3 NormalWeight(Vector3 position, Vector3 speed, float numCells, float directionScalar){ float weight = 0.0f; float startWeight = 0.0f; float totalSteps = (float)numCells; Vector3 nextPosition; Vector3 cumulativeNormal = Vector3.Zero; for (int i = 0; i <= numCells; i++) { // get position in next cell nextPosition = ProjectedXZ(position, speed, directionScalar); if (i == 0){ // current cell startWeight = CellWeight(position, nextPosition); weight = startWeight/totalSteps; } else if (i == numCells) // end cell weight = (1.0f - startWeight)/totalSteps; else // all cells in between weight = 1.0f/totalSteps; MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 432 433 cumulativeNormal+= weight * Normal(position); position = nextPosition; } cumulativeNormal.Normalize(); return cumulativeNormal; } ProjectedUp() drives the normal vector calculation for the ship from the game class. This method ensures that your ship is oriented properly above the terrain face: Vector3 ProjectedUp(Vector3 position, Vector3 speed, int numCells){ Vector3 frontAverage, backAverage, projectedUp; // total steps must be 0 or more. 0 steps means no shock absorption. if (numCells <= 0) return Normal(position); // weighted average of normals ahead and behind enable smoother ride. else{ frontAverage = NormalWeight(position, speed, numCells, 1.0f); backAverage = NormalWeight(position, speed, numCells,-1.0f); } projectedUp = (frontAverage + backAverage)/2.0f; projectedUp.Normalize(); return projectedUp; } ShipWorldMatrix() assembles the cumulative transformation for the space- ship. It performs the same scaling and translation routine that we have implemented in previous chapters. ShipWorldMatrix() also calculates the ship’s orientation according to both the ship’s direction and the slope of the terrain underneath. The di- rection matrix used is described in more detail in Chapter 8. These are the steps used to generate the direction matrix (refer to Figure 26-6): 1. Initialize a direction matrix using a fixed rotation about the Y axis. This is arbitrary but the direction vectors contained within this matrix will be corrected later. 2. Calculate the proper Up vector using a weighted average of leading and trailing normal vectors on the ship’s path, as shown in Figure 25-5. 3. Generate the Right vector from the initial Forward and weighted Up vector. 4. Calculate the proper Forward vector using the cross product of the Up and Right vectors. CHAPTER 25 Terrain with Height Detection MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 434 Add ShipWorldMatrix() to the game class to set the ship’s direction: Matrix ShipWorldMatrix() { float rotationAngle = (float)Math.Atan2(shipVelocity.Z, shipVelocity.X) + MathHelper.Pi / 2.0f; Matrix rotationY = Matrix.CreateRotationY(rotationAngle); Matrix scale = Matrix.CreateScale(0.3f, 0.3f, 0.3f); Matrix translation = Matrix.CreateTranslation(shipPosition); // 1. // generate direction matrix with fixed rotation about Y axis Matrix dir = Matrix.CreateRotationY(MathHelper.Pi); Vector3 velocity = Vector3.Normalize(shipVelocity); // 2. // get UP vector using weighted average of cells in object path const int CELL_SPAN = 3; // total trailing and leading cells dir.Up = ProjectedUp(shipPosition, velocity, CELL_SPAN); // 3. // FORWARD stores a fixed direction about Y but it is enough to // compute the RIGHT vector which is the normal of FORWARD & UP dir.Right = Vector3.Cross(dir.Forward, dir.Up); dir.Right = Vector3.Normalize(dir.Right); // 4. // Re-calculate FORWARD with known UP and RIGHT vectors dir.Forward = Vector3.Cross(dir.Up, dir.Right); FIGURE 25-6 Direction matrix 435 dir.Forward = Vector3.Normalize(dir.Forward); // apply other transformations along with direction matrix return scale * rotationY * dir * translation; } DrawModel() is needed in the game class to draw the ship. It draws the ship at the position and with the orientation to fit the terrain location and slope: void DrawModel(Model model){ // declare matrices Matrix world = ShipWorldMatrix(); foreach (ModelMesh mesh in model.Meshes){ foreach (BasicEffect effect in mesh.Effects) { // pass wvp to shader effect.World = shipMatrix[mesh.ParentBone.Index] * world; effect.View = cam.viewMatrix; effect.Projection = cam.projectionMatrix; // set lighting effect.EnableDefaultLighting(); effect.CommitChanges(); } // draw object mesh.Draw(); } } DrawShip() is called from the Draw() method: DrawModel(shipModel); When you run the program, your hills will appear, and as you move over them the camera will rise and fall with their elevation. The spaceship will travel back and forth riding the changes in terrain slope. As you can see, this impressive effect was created with very little effort. If you like the textures generated by the noncommercial version of Terragen, you should consider purchasing a license so you have the ability to create even larger im - age sizes and you can access more features. CHAPTER 25 Terrain with Height Detection C HAPTER 25 REVIEW EXERCISES To get the most from this chapter, try out these chapter review exercises. 1. Implement the step-by-step demonstration discussed in this chapter, if you have not already done so. 2. Reduce the CELL_SPAN value to 0 in ShipWorldMatrix() and run your game code. Notice the spaceship ride is much rougher because the normal vectors are not weighted. 3. Create your own height map. Load it into your application. To add detail, apply multitexturing to the terrain. 4. Modify the heightScale value inside TerrainContent.cs to heighten or flatten your terrain. 5. If you are feeling ambitious, try adjusting the camera’s view vector to change with the slope of the terrain just as the spaceship does. MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 436 CHAPTER CHAPTER 26 Animated Animated Models Models [...]... properly in the Solution Explorer (i.e., to use the content pipeline) FIGURE 26-6 MD2 project references FIGURE 26-7 Model properties 2 6 447 Animated Models C H A P T E R 448 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE Next, your Game1 .cs file has to reference the MD2 class to access it, so you must include the namespace for this class: using MD2Animation; To create an object to use this class for loading... 449 Animated Models C H A P T E R 450 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE Every frame, the md2 model vertices must be updated with a time-scaled interpolation between frames Adding the UpdateModel() instruction to the Update() method allows the MD2 class to take care of this interpolation to enable a smooth animation: md2.UpdateModel(graphics.GraphicsDevice, gameTime); The code used to draw the... selected You can also move the bone and attached mesh when the Move button is selected When the lamp is in FIGURE 26-3 Setup for the animation frames 443 Animated Models C H A P T E R 444 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE position to start the animation, you can set the keyframe from the Animate menu by selecting Set Keyframe To create the next keyframe, change the frame number in the left text... $skinheight 128 $skin lamp.bmp // sequences $sequence pivot 1 29 $sequence bowing 30 50 FIGURE 26-5 Side view of keyframes set at frames 30, 35, 40, 45, and 50 445 Animated Models C H A P T E R 446 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE When your md2.qc file has been created, place it in the same folder where your model’s texture is located You can now export your MD2 model to that directory by selecting File... triangle list data int offsetFrames; // offset to frame data int offsetglcmds; // offset to OpenGL command data int offsetEnd; // offset to end of file }; 439 Animated Models C H A P T E R 440 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE Each vertex in every frame is indexed The indexes are ordered in a sequence of triangle lists When the file is loaded, the indices are used to generate a list of vertex coordinates... viewport to add a joint where the cursor is placed To enable FIGURE 26-1 Two model pieces on the left; three joints and two bones for animating on the right 441 Animated Models C H A P T E R 442 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE proper mesh positioning with the bones (when animating your lamp model), you must add each of the three joints in sequence from the bottom to the top The first joint is placed... will be referenced later so you can play Zarlag’s animations on demand: public enum animations{ stand, run, attack, pain2, pain3, jump, pain1, flip, 451 Animated Models C H A P T E R 452 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE salute, taunt, wave, point, crstand, crwalk, crattack, crpain, crdeath, death1, death2, death3 } To load Zarlag instead of the lamp model, replace the LoadModel() instruction... currentGP.Triggers.Right> 0 && previousGP.Triggers.Right==0 && !md2.IsPlaying((int)animations.run)){ md2.SetAnimation((int)animations.run); // switch to run animation } 453 Animated Models C H A P T E R 454 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE This next code block triggers a one-time jump followed by a running loop when either the J key or right thumbstick is pressed Add this code to the Update() method to enable... md2Weapon.AdvanceAnimation(); md2Weapon.SetAnimation((int)animations.run); md2Weapon.SetAnimationSequence((int)animations.jump,(int)animations.run); 455 Animated Models C H A P T E R 456 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE md2Weapon.Resume(); md2Weapon.Pause(); Some minor changes are also needed in the DrawMD2Model() method It needs to be able to handle an identifier for the model so it knows... thumbstick on the game pad The MD2 class’s AdvanceAnimation() scrolls through the list of animations Add this code block to the Update() method to allow your users to view all animations for the Quake II file: // refresh previousKB previousGP currentKB currentGP new input states = currentKB; // store keyboard state from last frame = currentGP; // store previous gamepad state = Keyboard.GetState(); = GamePad.GetState(PlayerIndex.One); . Up and Right vectors. CHAPTER 25 Terrain with Height Detection MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 434 Add ShipWorldMatrix() to the game class to set the ship’s direction: Matrix ShipWorldMatrix() { float. divide by zero return 0.0f; // weighted distance in current cell relative to the entire MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 430 431 // distance to projected position return lengthToBorder / lengthToNewCell; } Since. - startWeight)/totalSteps; else // all cells in between weight = 1.0f/totalSteps; MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 432 433 cumulativeNormal+= weight * Normal(position); position = nextPosition; } cumulativeNormal.Normalize(); return

Ngày đăng: 02/07/2014, 06:20

Tài liệu cùng người dùng

Tài liệu liên quan