# Microsoft XNA Game Studio Creator’s Guide- P16

Chia sẻ: Cong Thanh | Ngày: | Loại File: PDF | Số trang:30

0
64
lượt xem
9

## Microsoft XNA Game Studio Creator’s Guide- P16

Mô tả tài liệu

Microsoft XNA Game Studio Creator’s Guide- P16:The release of the XNA platform and specifically the ability for anyone to write Xbox 360 console games was truly a major progression in the game-programming world. Before XNA, it was simply too complicated and costly for a student, software hobbyist, or independent game developer to gain access to a decent development kit for a major console platform.

Chủ đề:

Bình luận(0)

Lưu

## Nội dung Text: Microsoft XNA Game Studio Creator’s Guide- P16

1. 428 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE These next instructions belong inside LoadContent() to load the spaceship model: shipModel = Content.Load("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.
2. C H A P T E R 2 5 429 Terrain with Height Detection 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 FIGURE 25-5 Trailing and loading normal vectors
3. 430 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 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
4. C H A P T E R 2 5 431 Terrain with Height Detection // 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;
5. 432 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 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
6. C H A P T E R 2 5 433 Terrain with Height Detection 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
7. 434 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE FIGURE 25-6 Direction matrix 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);
8. C H A P T E R 2 5 435 Terrain with Height Detection 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.
9. 436 MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE 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.
10. CHAPTER 26 Animated Models
18. C H A P T E R 2 6 445 Animated Models Previewing Your Animation When the keyframes have been set, you can preview your animation by clicking the > button in MilkShape while the Anim button is pressed. When you are satisfied that the animation looks the way it is intended, you can export your model. Exporting to QUAKE II .md2 Format The XNA Quake II file loader included with this chapter requires that you export to Quake II MD2 format if you want to animate your model with this code. However, you can export to another model format from MilkShape in case you have a different type of loader. When exporting to the Quake II MD2 format, you must place an md2.qc file in the same directory where you export your model. The md2.qc file is used to help build the .md2 file. It contains information about the model name, the skin name, and skin pixel dimensions. It also contains the different animation names and their starting and ending frames. The md2.qc file is actually just a text file that you can edit in Notepad. You will have to edit this file or create one to list informa- tion about the image you use for your lamp and to document the frames used for the pivoting and bowing animations. If the md2.qc file is not present when you are ex- porting from MilkShape, you will receive an error. For this example, here are the re- quired contents for the md2.qc file: // Sample MD2 config, copy into export directory $modelname lamp.md2$origin 0.0 0.0 0.0 // skins $skinwidth 128$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