XNA 4 3D Game Development by Example:Beginner's Guide
上QQ阅读APP看书,第一时间看更新

Time for action – creating the Maze classes

  1. Add a new class file called Maze.cs to the Cube Chaser project.
  2. Add the following using directives to the top of the Maze.cs class file:
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
  3. Add the following fields to the Maze class:
    #region Fields
    public const int mazeWidth = 20;
    public const int mazeHeight = 20;
    
    GraphicsDevice device;
    
    VertexBuffer floorBuffer;
    
    Color[] floorColors = new Color[2] { Color.White, Color.Gray };
    #endregion
  4. Add a constructor for the Maze class:
    #region Constructor
    public Maze(GraphicsDevice device)
    {
        this.device = device;
        
        BuildFloorBuffer();
    }#endregion
  5. Add the following region and helper methods to the Maze class:
    #region The Floor
    private void BuildFloorBuffer()
    {
        List<VertexPositionColor> vertexList = 
            new List<VertexPositionColor>();
    
        int counter = 0;
    
        for (int x = 0; x < mazeWidth; x++)
        {
            counter++;
            for (int z = 0; z < mazeHeight; z++)
            {
                counter++;
                foreach (VertexPositionColor vertex in 
                    FloorTile(x, z, floorColors[counter % 2]))
                {
                    vertexList.Add(vertex);
                }
            }
        }
    
        floorBuffer = new VertexBuffer(
            device, 
            VertexPositionColor.VertexDeclaration, 
            vertexList.Count, 
            BufferUsage.WriteOnly);
    
        floorBuffer.SetData<VertexPositionColor>(vertexList.ToArray());
    }
    
    private List<VertexPositionColor> FloorTile(
        int xOffset, 
        int zOffset, 
        Color tileColor)
    {
        List<VertexPositionColor> vList = 
            new List<VertexPositionColor>();
    
        vList.Add(new VertexPositionColor(
            new Vector3(0 + xOffset, 0, 0 + zOffset), tileColor));
        vList.Add(new VertexPositionColor(
            new Vector3(1 + xOffset, 0, 0 + zOffset), tileColor));
        vList.Add(new VertexPositionColor(
            new Vector3(0 + xOffset, 0, 1 + zOffset), tileColor));
    
        vList.Add(new VertexPositionColor(
            new Vector3(1 + xOffset, 0, 0 + zOffset), tileColor));
        vList.Add(new VertexPositionColor(
            new Vector3(1 + xOffset, 0, 1 + zOffset), tileColor));
        vList.Add(new VertexPositionColor(
            new Vector3(0 + xOffset, 0, 1 + zOffset), tileColor));
    
        return vList;
    }
    #endregion

What just happened?

So far, we have not really defined anything about the actual maze associated with the Maze class other than the width and height of the maze we will be generating. The goal at this point is to build the floor of the maze and then bring our Maze and Camera classes together to allow us to display something to the game screen.

After we have generated all of the triangles necessary for our floor, they will be stored in the floorBuffer field. This field is a VertexBuffer, which holds a list of 3D vertices that can be sent to the graphics card in a single push.

Tip

Drawing with triangles

While we want to draw square floor tiles, the graphics card really only works with triangles. Even the most complex 3D models are made up of thousands or millions of small triangles. Fortunately, a square can be easily created with two equally-sized right triangles placed next to each other. Even when we load and display complex 3D models they are actually composed of lots of small triangles positioned to make up the surface of the object we are displaying.

In order to fill out this VertexBuffer, we need to generate the points that make up the triangles for the floor. This is the job of the BuildFloorBuffer() method, and its helper, FloorTile(). BuildFloorBuffer() begins by defining a List of VertexPositionColor objects. The built-in vertex declarations in XNA allow for different combinations of color, texture, and normal vectors to be associated with the position of the vertex (no matter what else a vertex has, it will always have a position). As the name implies, a VertexPositionColor defines a vertex with a position in 3D space and an associated color.

We will determine the color of the vertices (and thus the triangles and the squares) on the floor of the maze by alternating between white and gray, picking the colors from the floorColors list as the vertices are built.

The vertices for each square are built by calling the FloorTile() method, which returns a list of VertexPositionColor objects. Because we need two triangles to make up a square, we need to return six VertexPositionColor elements. We will use a similar technique when we build the maze walls later in this chapter.

The FloorTile() method accepts the X and Z offsets for this tile (if we were looking down at the maze from above, the number of squares across and down the maze we are building this square for) and the color for this particular tile. It then builds a new set of VertexPositionColor objects by adding six new vertices, three for each triangle, to a List object. The order that we define the vertices, called the winding order, is important. The vertices of each triangle need to be specified in a clockwise direction based on the angle from which the triangle will be viewed. The graphics device considers triangles to be single-sided entities. If we were to swing our camera underneath the maze, the floor would completely disappear.

What just happened?

In the previous image, we can see that we need four vertices to define the two triangles which compose a single floor square. We build the first triangle from the upper-left, upper-right, and lower-left vertices, and the second triangle from the upper-right, lower-right, and lower-left vertices, in those orders.

In the FloorTile() method mentioned previously, we explicitly add these six points to the list of vertices that will be returned from the function, offsetting the X and Z values of each vertex by the values passed into the function. This has the effect of translating the triangles to their appropriate positions in the 3D world; otherwise they would be stacked all on top of each other near the world origin.