2. Rendering the Object
Everything is fully initialized now and we're ready
to draw the square to the screen. Because we are only using vertex
colors without textures, there is nothing to read in the LoadContent function, so we can leave this alone. We have nothing to update at the moment, either, so let's move straight on to the Draw function.
The screen is cleared (to CornflowerBlue once again) as it was for sprites, but the approach we take to drawing now is very different. Instead of the SpriteBatchBasicEffect that we created earlier to manage the drawing for us. object, we use the
Each effect can contain one or more techniques.
These are the specific rendering operations that are contained within
the effect—the effect acting as a container for one or more techniques.
Each of the effects provided with XNA for Windows Phone 7 contains just
a single technique, so we don't need to pay much attention to this. We
will just use the default technique that the effect provides for us.
Finally, each technique contains one or more passes
that perform the actual rendering to the screen. If the rendering of an
effect needs to perform multiple updates to the content of the screen
in order to render, there will be multiple passes returned from the
technique, each of which will need to be drawn. BasicEffect
uses only one pass, but just for good form we will set our code to loop
for all passes that might be returned from the effect, to save
confusion later on when we do encounter multiple-pass effects.
Bearing all that in mind, the code required to render the square is shown in Listing 7. Once the pass has been determined, its Apply method is called to tell XNA to activate it. The code then calls DrawUserPrimitives, telling it the type of primitive that it is rendering, and passing various details about what to draw. The parameters for the DrawUserPrimitives function are as follows:
primitiveType contains the type of primitive that we wish to draw. In this case, we draw a TriangleStrip. The available primitives will be discussed in the next section.
vertexData allows us to pass the array of vertices that we have defined.
vertexOffset
is a value that allows us to start considering the vertices at a
position within the array other than its start. We are not using this,
so we just pass 0.
primitiveCount
is the number of primitives that we are drawing. As we specified that
XNA should draw triangles, setting this to 2 means to draw 2 triangles.
Remember that this is counting primitives, not vertices.
The code for the Draw function is shown in Listing 7.
Example 7. Drawing the colored square
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue);
foreach (EffectPass pass in _effect.CurrentTechnique.Passes) { // Apply the pass pass.Apply(); // Draw the square GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _vertices, 0, 2); }
base.Draw(gameTime); }
|
The resulting graphic can be seen in Figure 2.
Notice how XNA has handled the colors within the
rendered square. Each vertex is colored exactly as we had requested,
but between them XNA performs a smooth fade between the colors. This is
known as color interpolation and is
something that you will see again in the future: any vertex parameters
such as colors that differ from one vertex to the next will result in a
smooth fade as XNA renders between them. This can be very useful and
attractive, as this example demonstrates.
3. Moving the Object
To achieve this, we first need to track the rotation angle. We will do this by adding a class-level float variable named _angle, and will update it by 5 degrees each update, as shown in Listing 8.
Example 8. Updating the angle of the square
protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();
_angle += MathHelper.ToRadians(5);
base.Update(gameTime); }
|
To apply the angle to the square, we need to update the world matrix . Because we want to rotate the square, we need to give it a rotation matrix. XNA's MatrixCreateRotationZ function. This function accepts a single parameter (the rotation angle) and returns a matrix ready for us to use. class provides various methods for creating such a matrix, and the one we will select for our example is the
The updated code to draw the square with rotation is shown in Listing 9.
Example 9. Rotating and drawing the colored square
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue);
// Set the world matrix so that the square rotates _effect.World = Matrix.CreateRotationZ(_angle);
foreach (EffectPass pass in _effect.CurrentTechnique.Passes) { // Apply the pass pass.Apply(); // Draw the square GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _vertices, 0, 2); }
base.Draw(gameTime); }
|
Note that the call toDrawUserPrimitives
that is actually drawing the square is completely unchanged; it is the
state of the effect that is causing the object to rotate, not the
instruction to draw. This is clearly different to the approach we used
with sprite-based rendering.
4. Adding some Sparkle
Of course, this rotating square only scratches the
surface of what we can achieve with XNA. Let's make a simple change to
the project that results in a dramatic and attractive enhancement to
the displayed graphics.
If we modify the Draw code so that it is as shown in Listing 10, we will see that it has a significant effect on the graphics that are drawn to the screen, as shown in Figure 3. The code for this can be found in the NestedSquares example project.
Example 10. Rendering the square in the NestedSquares example project.
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue);
// Reset the world matrix _effect.World = Matrix.Identity;
// Loop for each square for (int i = 0; i < 20; i++) { foreach (EffectPass pass in _effect.CurrentTechnique.Passes) { // Apply a further rotation _effect.World = Matrix.CreateRotationZ(_angle) * _effect.World; // Scale the object so that it is shown slightly smaller _effect.World = Matrix.CreateScale(0.85f) * _effect.World;
// Apply the pass pass.Apply(); // Draw the square GraphicsDevice.DrawUserPrimitives (PrimitiveType.TriangleStrip, _vertices, 0, 2); } }
base.Draw(gameTime); }
|
The screen shots sadly don't do justice to the
effect project in operation; it is much better in motion than in still
images, but this gives an idea of the patterns that this tiny piece of
code is able to generate.
All that the loop is doing is drawing 20
shapes instead of one, each of which is slightly smaller than the last
and rotated to a different angle. The scale and rotate operations are
cumulative, meaning that, although the first (largest) square is
rotated by the angle specified in _angle, the second square is rotated by double this angle, the third by three times the angle, and so on.