Rendering Sprites

Direct3D already has an object that can be thought of as a sprite, namely the texture. However, textures must be rendered onto some type of primitives in order to be seen by the user. The Sprite class handles this work for you. Add the following variables for your application:

 private Texture spriteTexture; private Sprite sprite; private Rectangle textureSize; 

These obviously store the texture that will be used to render the sprite, as well as the sprite itself. You will also need to know the size of the texture in order to render it correctly, so that will be stored as well. At the end of your initialization method, directly after the device has been created, you will need to initialize these variables as follows:

 // Create our texture spriteTexture = TextureLoader.FromFile(device, @"..\..\logo.tga"); using (Surface s = spriteTexture.GetSurfaceLevel(0)) {     SurfaceDescription desc = s.Description;     textureSize = new Rectangle(0, 0, desc.Width, desc.Height); } sprite = new Sprite(device); 

You will find the logo.tga file located on the enclosed CD. After the texture has been created, we will need to determine its size. We retrieve the underlying surface and use the description of this surface to store our texture's size. Finally, we create an instance of the sprite class.

Each instance of the sprite class can draw zero to many sprites, by using various textures. Rather than duplicate our sprite class and textures, we will create a new class to handle our multiple sprites. Add the class found in Listing 16.2.

Listing 16.2 The Custom Sprite Class
 public class GraphicsSprite {     // static data for our sprites     private static readonly Vector3 Center = new Vector3(0, 0, 0);     private static readonly Random rnd = new Random();     // Instance data for our sprites     private Vector3 position;     private float xUpdate = 1.4f;     private float yUpdate = 1.4f;     public GraphicsSprite(int posx, int posy)     {         position = new Vector3(posx,posy,1);         xUpdate += (float)rnd.NextDouble();         yUpdate += (float)rnd.NextDouble();     }     public void Draw(Sprite sprite, Texture t, Rectangle r)     {         sprite.Draw(t, r, Center, position, Color.White);     } } 

Sprites can be rotated (even though this code won't perform this operation). When you draw the sprites, one of the parameters that must be passed in is the center of the sprite for rotation calculation. Rather than creating a new vector every time draw is called, it's easier to just store this here. The code should also add a little randomness to the sprites so that they don't appear to run the exact same way every time.

The class will need to store some data about each sprite it will be responsible for, namely the current position of the sprite and the speed at which it's moving. Separate speeds for both x and y will be stored so that the sprite won't be forced to move in 45-degree angles. Each is assigned a base value, but that will be randomly incremented.

The constructor takes the initial position of this sprite, and updates the speeds randomly. The only method left is where the actual drawing will take place. As you can see, the method is extremely simple. The method takes in the sprite and texture we will be using, as well as the size of the texture we stored. The color parameter is used to determine how to "shade" the rendered texture. Full white (as done here) will render the texture as it appears normally. Using red will give the texture a red tint as it's rendered. This color can be thought of like the materials you use for the meshes.

With the wrapper sprite class, the code can be updated to include drawing of the sprites. Before that happens, though, you will need to add a new variable to store the sprites you'll be rendering:

 System.Collections.ArrayList ar = new System.Collections.ArrayList(); 

For ease of use, the ArrayList class can't be beat. Of course, an empty array list won't do much good, so you'll need to add some sprites. Add the following code into your initialization method directly after you've created your sprite:

 // Add a few sprites ar.Add(new GraphicsSprite(0,0)); ar.Add(new GraphicsSprite(64,128)); ar.Add(new GraphicsSprite(128,64)); ar.Add(new GraphicsSprite(128,128)); ar.Add(new GraphicsSprite(192,128)); ar.Add(new GraphicsSprite(128,192)); ar.Add(new GraphicsSprite(256,256)); 

Feel free to modify the number and location of the sprites you'll be adding. With sprites in the collection, the application can finally render something. In the OnPaint override after the BeginScene call, add the following code to render your sprites:

 // Begin drawing our sprites with alpha blend sprite.Begin(SpriteFlags.None); // Draw each sprite in our list. foreach(GraphicsSprite gs in ar)     gs.Draw(sprite, spriteTexture, textureSize); // Notify Direct3D we are done drawing sprites sprite.End(); 

Before any sprites can be drawn, you need to tell Direct3D that you are about to render them. The Begin method accomplishes this. Then for every sprite in the collection, the Draw method is called. Every Begin call must be matched with an End call, so naturally one is included here.

With that out of the way, you could now run the application. One of the first things you'll probably notice is that the sprites don't seem to move, even though we're storing some speed information about them. Obviously, if nothing is ever done with this information, it's pretty useless. Add the method found in Listing 16.3 into the GraphicsSprite class.

Listing 16.3 Updating Sprites
 public void Update(Rectangle textureSize) {     // Update the current position     position.X += xUpdate;     position.Y += yUpdate;     // See if we've gone beyond the screen     if (position.X > (Form1.ScreenWidth - textureSize.Width))     {         xUpdate *= -1;     }     if (position.Y > (Form1.ScreenHeight - textureSize.Height))     {         yUpdate *= -1;     }     // See if we're too high or too the left     if (position.X < 0)     {         xUpdate *= -1;     }     if (position.Y < 0)     {         yUpdate *= -1;     } } 

In the update method, the position of the sprite is updated by the two separate speed variables. The position is then checked against the boundaries of the screen to make sure the sprite hasn't moved off screen, and if it has (or is about to), the direction is reversed. This will cause a "bouncing around the screen" effect on the sprites. This method will still need to be called, though, so add the following to the beginning of the OnPaint override:

 // Before we render each sprite, make sure we update them foreach(GraphicsSprite gs in ar)     gs.Update(textureSize); 

RENDERING SPRITES WITH ALPHA

If you are using the texture that is included on the CD, you've probably noticed the white around the texture that covers everything. This texture has alpha (or transparency) information stored in it, which will cause the border to be transparent. Turning on support for this feature is quite simple. Change the following method:

 sprite.Begin(SpriteFlags.None); 

Instead, use this:

 sprite.Begin(SpriteFlags.AlphaBlend); 

Rendering the scene now will produce much more favorable results. See Figure 16.1.

Figure 16.1. Bouncing sprites.

graphics/16fig01.jpg

Much like the actual draw calls, Update is called on each sprite in the collection. Running the application now will have your sprites bouncing all over your screen.



Managed DirectX 9 Graphics and Game Programming, Kick Start
Managed DirectX 9 Kick Start: Graphics and Game Programming
ISBN: B003D7JUW6
EAN: N/A
Year: 2002
Pages: 180
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net