5. Partial Image Rendering
Sprites can draw just a subsection of their images,
too. One of the main uses for this is for animation: multiple animation
frames can be placed into a single image, and then individual frames can
be rendered to the screen in sequence to animate the sprite.
An example of how this can be used is in the AnimationFrames project. Its Content project contains a single image, Radar.png, which can be seen in Figure 4.
As this figure shows, there are eight individual
images contained within the file. Each has exactly the same width (75
pixels), so we can draw any frame by locating its left edge and then
drawing a section of the image that is 75 pixels wide. To find the edge,
we take the animation frame number (from 0 to 7) and multiple it by the
frame width (75 pixels). Frame 0 therefore has a left edge of 0 pixels,
frame 1 a left edge of 75 pixels, frame 2 a left edge of 150 pixels,
and so on.
Drawing a section of the image is easy and is achieved by passing a Rectangle structure to the sourceRectangle property of Draw.
This defines the left and top corner of the rectangle within the source
image, and also the width and height to copy. The code that performs
the frame drawing in the AnimationFrames project is shown in Listing 6.
Example 6. Rendering a sprite using a source rectangle
_spriteBatch.Draw(_radarTexture, new Vector2(100, 100),
new Rectangle(_animationFrame * 75, 0, 75, 75), Color.White);
|
Although it is not shown in Listing 2-18, it is possible to combine the use of a source rectangle with scaling and rotation because all the versions of the Draw method that allow these operations to be specified also accept a sourceRectangle parameter. In fact, we have been passing this in all the examples using these techniques, except that we passed null to indicate that we wanted the whole image to be drawn rather than a subsection.
6. Layer Depth
If overlapping sprites are rendered using any of the
code samples so far, each will be rendered in front of the sprites
already on the screen. Sometimes you need to order the sprites in a
particular way so that some sprites appear in front of other sprites.
XNA provides an easy way to implement this. Some of the Draw overloads accept a float parameter called layerDepth,
and we can pass a value for this between 0 and 1 to control which
sprites appear in front of others. A value of 0 puts the sprite right at
the front, whereas a value of 1 puts it right at the back.
The only catch to this is that layerDepth processing is disabled by default. To enable it, we need to pass some additional parameters to the SpriteBatch.Begin call. The parameter that switches this on is the sortMode parameter, which we will set to SpriteSortMode.BackToFront.
It instructs XNA to draw the objects at the back first and then work
toward the front, allowing each sprite to draw over the top of the
sprites behind. When this parameter is passed, Begin also requires a value for its blendState parameter; we will pass BlendState.AlphaBlend, which is the default for this property when the overload of Draw with no parameters is used.
Listing 7
shows this effect in operation. Although the sprites are drawn from
left to right (which would normally result in the rightmost sprite
appearing in the front), we are specifying layerDepth values that put the leftmost sprite in front and the rightmost sprite at the back. You can find the full code for this in the LayerDepth example project.
Example 7. Drawing sprites with layerDepth sorting enabled
// Begin a sprite batch with BackToFront sorting enabled
_spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
// Draw some sprites with different layerDepth values
_spriteBatch.Draw(_smileyTexture, new Vector2(100, 100), null, Color.White, 0.0f,
Vector2.Zero, 1.0f, SpriteEffects.None, 0.0f);
_spriteBatch.Draw(_smileyTexture, new Vector2(140, 100), null, Color.White, 0.0f,
Vector2.Zero, 1.0f, SpriteEffects.None, 0.5f);
_spriteBatch.Draw(_smileyTexture, new Vector2(180, 100), null, Color.White, 0.0f,
Vector2.Zero, 1.0f, SpriteEffects.None, 1.0f);
// End the sprite batch
_spriteBatch.End();
|
The end result is shown in Figure 5.