Time for action – drawing the floor
- Add the
Draw
region and theDraw()
method to theMaze
class:#region Draw public void Draw(Camera camera, BasicEffect effect) { effect.VertexColorEnabled = true; effect.World = Matrix.Identity; effect.View = camera.View; effect.Projection = camera.Projection; foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply(); device.SetVertexBuffer(floorBuffer); device.DrawPrimitives( PrimitiveType.TriangleList, 0, floorBuffer.VertexCount / 3); } } #endregion
- In the
CubeChaserGame
class, add the following declarations to the declarations area of the class:Camera camera; Maze maze; BasicEffect effect;
- In the
Initialize()
method of theCubeChaserGame
class, initialize the camera, maze, and effect objects, placing this code before the call tob
ase.Initialize()
:camera = new Camera( new Vector3(0.5f, 0.5f, 0.5f), 0, GraphicsDevice.Viewport.AspectRatio, 0.05f, 100f); effect = new BasicEffect(GraphicsDevice); maze = new Maze(GraphicsDevice);
- In the
Draw()
method of theCubeChaserGame
class, add a call to draw the maze after theGraphicsDevice.C
lear()
statement:maze.Draw(camera, effect);
- Execute the Cube Chaser game project:
What just happened?
The Maze
class' Draw()
method accepts two parameters: the camera that will be used as the viewing point for drawing the maze, and an object called a BasicEffect
. Whenever we draw anything with XNA's 3D drawing system, we need an effect associated with what we are going to display.
An effect describes to the rendering system how the pixels on the display should be constructed based on the code for each particular effect. Effects are constructed with a mini-programming language called High Level Shader Language (HLSL), and can produce a wide variety of surfaces and special effects. We will touch a bit on effects and how they work in more detail in Chapter 5, Tank Battles – A War-Torn Land, but for now, the BasicEffect
class which is built into XNA contains everything we need for Cube Chaser.
In order to use our BasicEffect
, we need to specify a few parameters that instruct it how it should view the 3D scene and what to do with the vertices and triangles we give it.
First, we set VertexColorEnabled
to true
, since we are going to rely on the colors we passed into our FloorTile()
method to create the checkerboard effect for the floor.
Next, we need to specify three matrices for the effect. The first of these, the World matrix, we set to Matrix.Identity
. This special matrix is similar to multiplying a number by one. You get the same original number as the result. The World matrix allows us to transform everything we are drawing with the effect. Because we specified the absolute coordinates we want for our floor tiles when we created them, we do not want to transform them with the World matrix. We set the View and Projection matrices equal to the View and Projection matrices that our Camera
class has calculated for us.
Any given effect can contain multiple techniques, each potentially completely unrelated to the other techniques in the effect file. In the case of the BasicEffect
, we are using the default technique.
Each technique can additionally be composed of multiple passes. Each pass runs sequentially, building up the final image as its particular shader effects are applied. The default technique for the BasicEffect
class uses a single pass, but as written, our code could support more advanced techniques that iterate over multiple passes.
When the pass begins, we call the Apply()
method to signal that the pass has begun. Next, we tell the graphics device about the vertex buffer we wish to use to draw our maze floor using the SetVertexBuffer()
method.
Finally, we call DrawPrimitives()
to cause the graphics device to interpret the vertex buffer and output the triangles it contains to the graphics card. We do this by specifying that we are drawing a list of triangles (PrimitiveType.TriangleList
), beginning with the first element in the vertex buffer (element 0), and drawing a number of triangles equal to the number of vertices divided by three (because each triangle is composed of three vertices).
In order to utilize the Draw()
method that we have added to the Maze
class, we need to set up a few items in the CubeChaserGame
class, including the camera we will be using, an instance of the maze itself, and the BasicEffect
object that we will pass to the Draw()
method.
When the camera is initialized during the Initialize()
method, we pass it a location (0.5f, 0.5f, 0.5f)
that will place the camera directly in the center of the upper-left corner of the maze, one half of a unit off the floor.
The second parameter for the camera constructor is the beginning rotation angle for the camera, which we specify as 0. Recall that our camera code specifies that without any rotation, our camera will be facing along the positive Z axis, looking along the side of the maze, which grows ahead and to the left of us.
The remaining camera parameters specify the aspect ratio (which we simply pass along from GraphicsDevice.Viewport.AspectRatio
, which will correspond to the aspect ratio of the window or full screen resolution we are using), and the near and far clipping plane distances. Here, we specify that anything closer to the camera than 0.05f
units will not be drawn, and the maximum distance we will consider for drawing anything is 100f
units. Since our entire maze will be contained within a 20x20 unit area, this means we could theoretically see the entire thing from any point in the maze.