Note: The assumption is being made that previous tutorials have already been completed and that a project is available
and in a state to make the changes to be laid out in the current tutorial. That way the same classes and references do not
need to be made in each tutorial, but instead new classes can be added and existing classes and procedures can be expanded
upon.
2D Game Development with Managed DirectX: Tutorial 02 - Creating a 2D Sprite
In this tutorial a you will again see the wonderfully rendered black form, but on top of that black will be a sprite
object with a texture applied. To display a 2D Sprite, we will be making use of the Sprite object and so a new reference
to another Microsoft DirectX dll will be required. So that's where we will start.
::Add the DirectX References::
A new DirectX dll is required for displaying a 2D sprite, so double-click on "My Project" and add a new reference to your
project.
Referenced Dlls:
Microsoft.Direct3DX
I've provided the exact dll version of the dll, in this case I use the 1.0.1901.0 version of the Managed DirectX dlls.
Again, no particular reason and you can probably use the latest versions from the SDK with some tweaks to the code where
functions and parameters have changed.
::Creating the DXSimpleSprite class::
Start by adding a new class to your project called "DXSimpleSprite". This class will store and manage the information
important to store about a sprite object. Yet, the class itself will not have a sprite object in it. The reason for this
decision is to limit the number of objects tht must be created in a game project. As you play around with game development
you will start to look for ways to limit expensive objects and really be forced to use good objected oriented development
practices.
To begin to construct the DXSimpleSprite class, we'll add two class level objects, one for storing a texture and one for
storing a rectangle.
Class level objects and properties in DXSimpleSprite
'The rectangular area of a texture that will be displayed on a sprite
object
Private mSourceRectangle As Rectangle
Public ReadOnly Property SourceRectangle() As Rectangle
Get
Return mSourceRectangle
End Get
End Property
'The texture to be applied to a sprite object
Private mTexture As Microsoft.DirectX.Direct3D.Texture
Public ReadOnly Property Texture() As Microsoft.DirectX.Direct3D.Texture
Get
Return mTexture
End Get
End Property
A Texture is a DirectX object. In the most basic sense, a texture is kind of like a blanket with an image on it. You can
then put that blanket over a cardboard box and each side of that box will display some of that image on that blanket. In
2D game development we only pay attention to one face of the box and we decide which portion of the blanket we want to
display on that side of the box by setting a source rectangle. The source rectangle defines which part of the texture we
want to display. Or in the real world example I used, what part of the blanket we want to move around to have that part of
it's image display on the particular face of the box we are looking at.
Now, let's move on to making the class construction.
Class constructors for DXSimpleSprite
'Description: The class constructor. Store the source rectangle and load an image
into a texture.
Public Sub New(ByVal theDevice As Microsoft.DirectX.Direct3D.Device, ByVal theTextureName As String, ByVal isLoadedFromFile As Boolean, ByVal theHeight As Integer, ByVal theWidth As Integer)
'Store the rectangle for the sprite, this is the size of a rectangle on an
image that makes up the
'texture for the sprite.
mSourceRectangle = New Rectangle(0, 0, theWidth, theHeight)
'Load the image, either from a file or from the project in a
stream
If isLoadedFromFile = True Then
mTexture = Microsoft.DirectX.Direct3D.TextureLoader.FromFile(theDevice, theTextureName,
theWidth, theHeight, 24, 0, Microsoft.DirectX.Direct3D.Format.Unknown, Microsoft.DirectX.Direct3D.Pool.Default, Microsoft.DirectX.Direct3D.Filter.None, Microsoft.DirectX.Direct3D.Filter.None, 0)
Else
mTexture = Microsoft.DirectX.Direct3D.TextureLoader.FromStream(theDevice, Me.GetType().Assembly.GetManifestResourceStream(theTextureName))
End If
End Sub
The constructor for DXSimpleSprite takes a couple of parameters. First the device object. This is necessary so that a
texture object can be created, but it is not necessary for the DXSimpleSprite class to know how to create a device, it
just needs to make use of one already created.
The name of the texture will either be a path to an image file on disk or the name of a texture embedded as a stream in
the project. The final parameter 'isLoadedFromFile' helps the constructor to determine which type of texture name has been
passed into the constructor.
The height and the width passed in are used to construct the source rectangle. They help define what part of the texture
we want to display on the sprite object.
So in the constructor, a source rectangle has been defined and stored and a texture object has been created either from
file or from a stream. Typically in a project you would probably only use file or stream exclusively, but I wanted to
demonstrate both methods of loading a texture.
Well, there really isn't much more to the DXSimpleSprite class, we've constructed it and now all that's left is destroying
it and disposing of any objects it created.
Class Destructor and Dispose procedure DXSimpleSprite
'Description: Class destructor, destroy the objects
Protected Overrides Sub
Finalize()
Call Dispose()
MyBase.Finalize()
End Sub
'Description: Dispose of the objects created in the class
Private Sub Dispose()
mSourceRectangle = Nothing
If Not (mTexture Is Nothing) Then
mTexture.Dispose()
End If
mTexture = Nothing
End Sub
That's all we need for the new DXSimpleSprite class, now we need to begin enhancing other parts of the project to work
with this newly created class.
::Enhancing the DX3DEngine class to support Sprites::
Now that we have a DXSimpleSprite class, the DX3DEngine needs to be enhanced to work with sprites. This will be
accomplished by adding new sprite object and a procedure to draw a sprite. Again, by placing the sprite object in the
Engine, only one sprite object will be needed for the entire project.
Sprite object and DrawSprite procedure for DX3DEngine
'A Direct3D sprite object
Private mSprite As Microsoft.DirectX.Direct3D.Sprite
Public Sub DrawSprite(ByVal theSprite As DXSimpleSprite)
If DeviceAvailable() = False Then
Exit Sub
End If
If mSprite Is Nothing Then
mSprite = New Microsoft.DirectX.Direct3D.Sprite(myD3DDevice)
End If
If mSprite.Disposed = True Then
Exit Sub
End If
mSprite.Transform = Microsoft.DirectX.Matrix.Identity
mSprite.Begin(Microsoft.DirectX.Direct3D.SpriteFlags.AlphaBlend)
mSprite.Draw(theSprite.Texture, theSprite.SourceRectangle, New
Microsoft.DirectX.Vector3(0.0F, 0.0F, 0.0F), New Microsoft.DirectX.Vector3(0.0F, 0.0F, 0.0F),
Color.White)
mSprite.End()
End Sub
The procedure begins similar to the "BeginRender" procedures that were created previously. It first checks to make sure
that a device is available for rending the sprite and if it isn't, the sub is exited so that errorr will not occur
rendering to a device that has been disposed or is currently unavailable.
A check is then made to see if the class level sprite object has been created and if it hasn't one (and only one) is made.
Next, a quick check to see if the sprite has been disposed. If this is the case then the application is most likely
shutting down so we again don't want to cause errors by trying to render a sprite that's been disposed.
Next comes some more DirectX mumbo jumbo code. These are important concept to learn when you begin advancing further in 3D
game development, but are not really necessary to be understood on any deep level for 2D development.
Tranform basically has to do with how the "cardboard box" needs to be changed or rotated. The DirectX help for
"Transform"is awesome because it says (and I quote) "A Matrix object used to transform a Sprite object.". Boy, if that
doesn't make it crystal clear what transform does....
Anyway, just know that we don't really want to "transform" our sprite at this moment and that's why we define the
Identitymatrix...so moving on. We want to actually draw a sprite now and we see a familar pattern beginning to take place.
Before you can draw something you have to "Begin" to draw, then draw and then "End" your drawing. This is a concept that
DirectX uses over and over.
You can see that the "Begin" takes a parameter. This is actually an important parameter and learning to play with the
different rendering flags you can pass in to the "Begin" procedure will help you achieve some different effects when
rendering sprites. For now we will just use one flag, "AlphaBlend" which is useful and necessary when rendering text.
(which we aren't but might want to at some point). I'm still learning a lot about the various flags and what they do
whenset in the Begin myself so as I discover new techniques and tricks I will try and share.
Now we actually draw or "render" the sprite object on the DirectX device object. You can see that the Draw function
usesquite a few of the properties of our DXSimpleSprite object and then a few defaults. First is uses the texture object
so that the appropriate "blanket" can be draped over our sprite "box". Then it looks at the Source Rectangle so it knows
which part of the texture "blanket" should be draped over the face of the box that is looking up at the screen. Next, it
wants the "center" and "position" and alpha "color". For now those properties are just defaulted because we are only
concerned with actually getting a sprite to render so we'll be revisiting some of those again in the future.
Then finally we "End" the rendering of the sprite.
Well, that's it for the DrawSprite procedure. Now we just need to be good little developers and add our new Sprite object
to the Dispose procedure so we can be sure we are keeping things neat and tidy in memory.
Add the new Sprite Object to the Dispose procedure for DX3DEngine
'Description: Dispose of the objects created in the class
Private Sub Dispose()
'Destroy the Present Parameters object
mPresentParams = Nothing
'Destroy the Direct3D Device object
If Not (mDirect3DDevice Is Nothing) Then
mDirect3DDevice.Dispose()
End If
mDirect3DDevice = Nothing
'Destroy the Sprite object
If Not (mSprite Is
Nothing) Then
mSprite.Dispose()
End If
mSprite = Nothing
End Sub
Now that good coding practices have been maintained, we can move on to the next step in this tutorial. We have a
DXSimpleSprite class, the DX3DEngine has been enhanced to draw a sprite so now the next thing we need to do is actually
create a DXSimpleSprite somewhere and get it drawn. This will be done in the "GameEngine" class.
::Enhancing the Game Engine class::
To start out, a new class level DXSimpleSprite object needs to be added.
DXSimpleSprite Class level object for GameEngine
Public Class GameEngine
Private mGraphics As DX3DEngine
Private mFirstSprite As DXSimpleSprite
Now, the object needs to be created in the Class constructor.
DXSimpleSprite object creation in Class constructor for GameEngine
'Description: The class constructor. Create the objects for the class
Public Sub New(ByVal theRenderTarget As Control)
mGraphics = New DX3DEngine(theRenderTarget, True)
'Create a new spirte object from a file
mFirstSprite = New DXSimpleSprite(mGraphics.myD3DDevice, "Textures/MyFirstSprite.PNG", True, 64, 64)
'Create a new sprite object from a resource stream
'mFirstSprite = New DXSimpleSprite(mGraphics.myD3DDevice,
"CreatingA2DSprite.MyFirstSprite.PNG", False, 64, 64)
End Sub
You can see that I've added to different creations of the sprite object, but one is commented out. I wanted to give both
methods of creating the texture from file and from a stream. Just play around with using both so you can get familiar with
the option available to you.
Well, now that we have the DXSimpleSprite object available, it can be rendered and we'll do that in the Render sub of
theGameEngine class.
Render the Sprite in GameEngine
'Description: Setup the device for rendering
Public Sub Render()
mGraphics.BeginRender()
mGraphics.DrawSprite(mFirstSprite)
mGraphics.EndRender()
End Sub
The actually rendering of the sprits really is as simple as using the new "DrawSprite" procedure we crated in the
DX3DEngine. The simple sprite object we've create in the GameEngine is passed in to the DrawSprite procedure and so it's
that information that will be used when the sprite is drawn on the device.
We've created the simple sprite object, we've drawn it to the device so what's left? I hope you screamed it out loud
because you're getting so used to it. WE DISPOSE OF THE OBJECTS WE CREATED IN OUR CLASS. Yep, that's correct. Let's make
sure we clean up our mess.
Dispose of the new DXSimpleSprite object for GameEngine
'Description: Dispose of any object created in the class
Private Sub Dispose()
mFirstSprite = Nothing
mGraphics = Nothing
End Sub
Well, that's it for the GameEngine, but don't compile and run just yet. There is still one more final thing we need to
take care of. An actual image is needed to be used fo the texture. We referenced an image called "MyFirstSprite.PNG" in
the constructor of the GameEngine class, but currently no such image exists.
::Adding the Images::
I've provided an image here, that can
be used. And I'll walk through the process of adding an image as an embedded resource to your project.
To start, in the Solution Explorer add a new folder to your project called "Textures". Next, drag an image from a folder
on your computer into that folder in your Solution Explorer. This will add that image to your project. Now select that
image, and change the "Build Action" property to "Embedded Resource".
By changing the build action to "Embedded Resource" for that image, you will be able to reference it's stream in code by
using a path like this "NameOfProject.ImageName.ImageExtension". In my case my project is called "CreatingA2Dsprite", my
image name is "MyFirstSprite" and it's extension is "PNG" so I can reference the image stream using
"CreatingA2DSprite.MyFirstSprite.PNG".
Well, that's it. Now that you have created the textures folder and moved the image into it, you can load it from the file
from that location OR you can load it as a stream since you've also set it's build action to "Embedded Resource".
::The Rendered Sprite::
Now you can run it and you should see the lovely image of stick man raising his hand in joy at being rendered in such
wonderful manner.
If only you could make him move....
The source code for the tutorial is available here.
While I begin work on the next tutorial, I would recommend playing with loading the images in both manners, making a few
DXSimpleSprite objects to be rendered and making a few of your own images to be used as textures.
Again, I'm still very new tutorials and any feedback is welcome. If you found the tutorial useful, have a suggestion or
just think they are a waste of time, please just take a few moments to leave me a comment. I appreciate the feedback!