Managed DirectX Tutorial 01: Setting up DirectX or "Gee, I can make black form."

2D Game Development with Managed DirectX: Tutorial 01 - Setting up DirectX

The first thing you need to prepare when creating a 2D game in Managed DirectX is the device. The Direct3D device is what you will be using to render all of your objects to the screen. It manages all the video card and display junk that you don't need to concern yourself with unless you're the ZMan and are really into that sort of thing.

For 2D games we can ignore most of that "extra" junk and just set the device up with the basics. In this tutorial, we'll create the main game loop, the device class and a game engine class. The results, you will get to see a form launched with the Direct3D device rendering a black background on it.

::Create the Solution::

To start the ball rolling, open up Visual Basic Express (free from Microsoft) and create a new project using the "Windows Application" template. Give it a snazzy name like "MyCoolGame" or "TheNextWorldOfWarcraft". I stuck with some very unoriginal and called it "SettingUpDirectX".



Next, because we really don't need it (well, we kind of do, but I don't like seeing it in the project list) remove the "Form1.vb" object from the solution. You'll have an error in the error list as a result ('Form1' is not a member of 'SettingUpDirectX') but don't worry, we will get that error cleaned up before the tutorial is complete.

::Add the DirectX References::

Well, since we are making a solution that is going to work with DirectX, we had better give it the power to do that. Double-click on the "My Project" object in the solution explorer. And add references to the DirectX dll we will be using in this tutorial.



Referenced Dlls:
Microsoft.DirectX
Microsoft.DirectX.Direct3D

I'm using the 1.0.1901.0 version of the Managed DirectX dlls. There isn't any particular reason that I'm using those, just that those are the one's I'm familiar with and I got tired of upgrading and having to redo my projects to work with the newer Managed DirectX dll releases. I've provided the versions of the dlls that I use so that you can get the same results that I do and later you can try to upgrade to a newer release version if that sort of thing makes your heart go pitter pat.

::Naming Standards::

Before we begin any coding, I want to spend a little time talking about naming standard. First, if you don't currently use any naming standards start. In my tutorials I will be attempting to use the following naming standards.


Class level objects are named using camel case and are prefixed with an "m".
Example:

Private mRenderTarget as Form


Properties for objects are named using camel case and are typically prefixed with "my"
Example:

Public Property myRenderTarget as Form


The exception for this is for Properties that wrap the more typical objects like integers, strings, etc.
Example:

Public Property Height as 

Integer


Procedure level objects are named using camel case and are prefixed with an "a"
Example:

Dim aAdapter as Integer


Objects being passed into a procedure are named using camel case and are prefixed with "the" or "is"
Example:

Public Sub CreateDevice(ByVal isWindowed as Boolean, ByVal theHeight as Integer, ByVal theWidth as Integer)


I've been using this naming standard for quite a while now so it's just what I'm used to. I like it because it's easy to determine the scope of an object and it's fairly simplistic. Do I care if you use the same standard? No, but I would stress that you come up with some kind of written standard and use it constantly and consistently no matter the size or importance of the project.

Ok, now that we have that behind us, let's write some code.

::Creating the DirectX3D Device Class::

Now that we the DirectX dll references made and I've shared with you my naming standard we will add our first "Class" object to the solution. I named my class "DX3DEngine" because it's an object that is going to manage the DirectX 3D device object and using the word "Engine" just makes it sound really cool and all powerful.

We will begin to add some substance to our DX3DEngine (see doesn't that sound awe inspiring?) but I'm not sure what order makes the most sense in creating the class so if it seems that I'm jumping around a bit or you're not sure why I added something in particular order, it's because I am and I don't either.

First start with creating the class level objects for the DX3DEngine

Class level objects and properties in DX3DEngine

    'The present parameters for the DX3D Device object
    Private mPresentParams As 

Microsoft.DirectX.Direct3D.PresentParameters = Nothing
 
    'Indicates whether the device has been lost
    Private mLostDevice As Boolean = False
 
    'The D3DDevice object
    Private mDirect3DDevice As Microsoft.DirectX.Direct3D.Device 

= Nothing
    Public ReadOnly Property myD3DDevice() As Microsoft.DirectX.Direct3D.Device
        Get
            Return mDirect3DDevice
        End Get
    End Property
 
    'The Control that is connected to the device and used as the render 

target
    Private mRenderTarget As System.Windows.Forms.Form = Nothing
    Public ReadOnly Property myRenderTarget() As System.Windows.Forms.Form
        Get
            Return mRenderTarget
        End Get
    End Property
 
    'The Heigth the device backbuffer
    Private mDeviceHeight As Integer = 

1024
    Public ReadOnly Property DeviceHeight() As Integer
        Get
            Return mDeviceHeight
        End Get
    End Property
 
    'The Width for the device backbuffer
    Private mDeviceWidth As Integer = 

768
    Public ReadOnly Property DeviceWidth() As Integer
        Get
            Return mDeviceWidth
        End Get
    End Property
 
    'The color the device will be "cleared" with
    Private mDeviceColor As Color = Color.Black
    Public Property DeviceColor() As Color
        Get
            Return mDeviceColor
        End Get
        Set(ByVal value As 

Color)
            mDeviceColor = value
        End Set
    End Property

Here we've created the objects for the DirectX3D device, an object to store the render target and a couple of other objects and properties dealing with the device.

Next we'll being making something procedures to initialize and manipulate these objects.

First we'll make the constructors for the class.

Class constructors for DX3DEngine

   'Description: The class constructor. Setup the device to run in either windowed mode 

or full screen 
    '             mode with the default class values for height and width
    Public Sub New(ByVal theRenderTarget As System.Windows.Forms.Form, ByVal isWindowed As Boolean)
        'If the device is to be run in FullScreen mode then the default heights and widths will be 

used
        Call Initialize(theRenderTarget, isWindowed, mDeviceHeight, 

mDeviceWidth)
    End Sub
 
    'Description: The class constructor. Setup the device to run in full screen mode witht he given 

height and width
    Public Sub New(ByVal theRenderTarget As System.Windows.Forms.Form, ByVal theHeight As Integer, ByVal theWidth As Integer)
        'The device will be setup to run in fullscreen mode with the heights and widths passed in 

to the constructor
        Call Initialize(theRenderTarget, False, 

theHeight, theWidth)
    End Sub

Here you can see the Dx3DEngine is created by passing in a form as a render target and indicating whether the device is going to run in Windowed mode or FullScreen (with the option of using the default height and width for Fullscreen mode or passing in a set height and width)

Since we are calling an Initialize procedure now might be an excellent time to created one and it might be a great idea if it looked something like this.

Initialize procedure for DX3DEngine

    'Description: Initialize the class objects
    Private Sub Initialize(ByVal 

theRenderTarget As System.Windows.Forms.Form, ByVal isWindowed As Boolean, ByVal theHeight As Integer, ByVal theWidth As Integer)
        'Store the rendering target
        mRenderTarget = theRenderTarget
 
        'Store the device backbuffer height and width
        mDeviceHeight = theHeight
        mDeviceWidth = theWidth
 
        'Create the DX3D Device object
        Call CreateDevice(isWindowed, theHeight, theWidth)
    End Sub

The Initialize procedure stores the render target, the height and the width and then creates device by calling the aptly named procedure "CreateDevice". The "CreateDevice" procedure is where the Direct3D device is created and setup and is the real meat and potatoes of the class so why don't we mosey over and take a look?

CreateDevice in DX3DEngine

   'Description: Create the DX3D Device object
    Private Sub CreateDevice(ByVal 

isWindowed As Boolean, ByVal theHeight As Integer, ByVal theWidth As Integer)
        'Use the system's default adapter
        Dim aAdapter As Integer = 

Microsoft.DirectX.Direct3D.Manager.Adapters.Default.Adapter
 
        'Setup the presentation parameters
        mPresentParams = New Microsoft.DirectX.Direct3D.PresentParameters
 
        'Determin whether the device will be run in fullscreen or windowed 

mode
        mPresentParams.Windowed = isWindowed
        If mPresentParams.Windowed = False Then
            'The device will be fun in fullscreen mode so additional presentation parameters need 

to be set
            mPresentParams.Windowed = False
            mPresentParams.BackBufferCount = 1
            mPresentParams.BackBufferWidth = theWidth
            mPresentParams.BackBufferHeight = theHeight
            mPresentParams.BackBufferFormat = Microsoft.DirectX.Direct3D.Manager.Adapters.Default.CurrentDisplayMode.Format
            mRenderTarget.Cursor.Dispose()
        End If
 
        mPresentParams.SwapEffect = Microsoft.DirectX.Direct3D.SwapEffect.Discard
        mPresentParams.PresentationInterval = Microsoft.DirectX.Direct3D.PresentInterval.Immediate
 
        'Creation flags
        Dim aFlags As 

Microsoft.DirectX.Direct3D.CreateFlags
 
        'See if we can use hardware vertex processing
        Dim aCaps As Microsoft.DirectX.Direct3D.Caps = 

Microsoft.DirectX.Direct3D.Manager.GetDeviceCaps(aAdapter, Microsoft.DirectX.Direct3D.DeviceType.Hardware)
        If aCaps.DeviceCaps.SupportsHardwareTransformAndLight = True Then
            aFlags = Microsoft.DirectX.Direct3D.CreateFlags.HardwareVertexProcessing
        Else
            aFlags = Microsoft.DirectX.Direct3D.CreateFlags.SoftwareVertexProcessing
        End If
 
        'Check to see if pure devices are supported
        If aCaps.DeviceCaps.SupportsPureDevice = True Then
            aFlags = aFlags Or 

Microsoft.DirectX.Direct3D.CreateFlags.PureDevice
        End If
 
        'All the data has been collected, create the device
        mDirect3DDevice = New Microsoft.DirectX.Direct3D.Device(aAdapter, 

Microsoft.DirectX.Direct3D.DeviceType.Hardware, myRenderTarget.Handle, aFlags, mPresentParams)
 
        'Hook up the device resizing event
        AddHandler myD3DDevice.DeviceResizing, New 

System.ComponentModel.CancelEventHandler(AddressOf OnDeviceResizing)
    End Sub

I tired to be liberal with my comments in the "CreateDevice" procedure to explain what is going on but I can sum it up like this, basically the parameters for the Direct3D device are discovered and/or set and then the device is created.

The last little piece of the procedure adds an event to handle resizing, so that's the next procedure we'll cover

OnDeviceResizing procedure for DX3DEngine

    'Description: Handles device resizing
    Private Sub OnDeviceResizing(ByVal 

sender As Object, ByVal e As System.ComponentModel.CancelEventArgs)
        e.Cancel = True
    End Sub

Basically all this procedure does is handle the resizing event when it fires, so lets move on to some more interesting code that handles the rendering.

When you want to start rendering objects to the screen, you typically start with a call to "BeginRender" and our engine provides just suce a procedure

BeginRender procedure for DX3DEngine

   'Description: Sets up the device for rendering. Clears the device and begins the 

scene
    Public Sub BeginRender()
        If DeviceAvailable() = True Then
            myD3DDevice.Clear(Microsoft.DirectX.Direct3D.ClearFlags.Target, DeviceColor, 1.0F, 0)
            myD3DDevice.BeginScene()
        End If
    End Sub

The "BeginRender" procedure first checks to make sure that the device is avaialble (we'll be making that proceudre in jsut a few minutes) and then Clears the device to the current color of the DeviceColor property we setup when we first started making the class. Then it tells the device to begin rendering the scene.

If we are going to begin rendering something, then I guess it makes sense to end rendering at some point as well and lo and behold that's what the next procedure we're going to look at does.

EndRender procedure for DX3DEngine

   'Description: Ends the rendering for the device. Ends the scene and presents it tot 

he render target
    Public Sub EndRender()
        If DeviceAvailable() = True Then
            myD3DDevice.EndScene()
            Call Present()
        End If
    End Sub

Again, before the attempting anything a check is made to make sure the device is available (and again, I promise we'll get to that). Then the device is told to end the scene. The next step is to present the scene to the render target since all rendering is now complete. That's what the "Present" procedure does and that is what we will look at next.

Present procedure for DX3DEngine

    'Description: Presents the rendered scene to the connected render 

target
    Private Sub Present()
        If DeviceAvailable() = True Then
            Try
                'Show the back buffer on screen
                myD3DDevice.Present()
            Catch ex As 

Microsoft.DirectX.Direct3D.DeviceLostException
                mLostDevice = True
            End Try
        End If
    End Sub

What do you know? Before an attempt is made to present the scene, we check yet again to see if the device is available first (we'll do that one next, I promise). Then we attempt to present the rendered screen to the render target (in our case a form). We wrap this in a Try..Catch.. because there's a chance that the device has been lost and we want to catch that error from bubbling up to the user. Instead we'll set an internal variable indicating the device is lost and we will attempt to recover it. Where do we do that? Why in the DeviceAvailable function (see I told you we'd get to it, you really need to learn some patience!)

DeviceAvailable procedure for DX3DEngine

    'Description: Determines if the device is available (not lost or 

disposed)
    Private Function DeviceAvailable() As Boolean
        DeviceAvailable = False
 
        'The device is not available if it has been disposed
        If myD3DDevice.Disposed = True Then
            Return False
        End If
 
        'The device is not available if it has been lost
        If mLostDevice = True Then
            Call Recover()
            Return False
        End If
 
        Return True
    End Function

In the "DeviceAvailable" procedure we determine if the device is available. Obviously if the device has been disposed then it's not available. Then we check to see if the lost device flag has been set, if so then we will make an attempt to recover it. If the device hasn't been disposed and it hasn't been lost, then it's avaialable.

Now we'll take a look at our attempt to recover a lost device.

Recover procedure for DX3DEngine

    'Decription: Attempt to recover the device when it has been lost
    Private Sub Recover()
        Dim aResult As Integer
        myD3DDevice.CheckCooperativeLevel(aResult)
 
        Select Case CType(aResult, 

Microsoft.DirectX.Direct3D.ResultCode)
            Case Microsoft.DirectX.Direct3D.ResultCode.DeviceLost
                mLostDevice = True
            Case Microsoft.DirectX.Direct3D.ResultCode.Success
                mLostDevice = False
            Case Microsoft.DirectX.Direct3D.ResultCode.DeviceNotReset
                'The device isn't lost anymore, but needs to be reset
                Try
                    myD3DDevice.Reset(mPresentParams)
                    mLostDevice = False
                Catch ex As 

Microsoft.DirectX.Direct3D.DeviceLostException
                    mLostDevice = True
                End Try
        End Select
    End Sub

Basically, you take the device and call it's CheckCooperativeLevel procedure. Depending on what result you get, you have either recovered the device, it's still lost or you have a chance of resetting the device to recover it.

Finally, we will write the final procedures of our DX3DEngine and they consist of the destruction and disposal of the objects created in the class. It's always a good practice to remember to clean up the mess you made.

Finalize and Dispose procedures for DX3DEngine

    '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()
        'Destroy the Present Parameters object
        mPresentParams = Nothing
 
        'Destroyt he Direct3D Device object
        If Not (mDirect3DDevice Is Nothing) Then
            mDirect3DDevice.Dispose()
        End If
        mDirect3DDevice = Nothing
    End Sub

There really isn't much to these. When the class is being destroyed, it first calls the Dispose procedure which destroys each of the objects created in this class.

Well, that's the DX3DEngine in it's entirety and we're just a hop skip and a jump from acutally being able to use the engine and see it's amazing results

::Creating the Game Engine class::

Add a new class to your solution and call it "GameEngine" because it sounds pretty cool and we already know that adding an "engine" to any class name makes it uber awesome.

To be honest for this simple tutorial, there really isn't a need to make all the seperations I'm making but I do it so you can start to think about each of the pieces in a seperate fashion.

The game engine's code is fairly simplistic and we'll look at it all at once and then discuss it.

GameEngine Class code

    Private mGraphics As DX3DEngine
 
    'Description: The class constructor. Create the objects for the class
    Public Sub New(ByVal theRenderTarget As Control)
        mGraphics = New DX3DEngine(theRenderTarget, True)
    End Sub
 
    'Description: Setup the device for rendering
    Public Sub Render()
        mGraphics.BeginRender()
        mGraphics.EndRender()
    End Sub
 
    'Description: The class destructor. Destroy the objects for the class
    Protected Overrides Sub 

Finalize()
        Call Dispose()
        MyBase.Finalize()
    End Sub
 
    'Description: Dispose of any object created in the class
    Private Sub Dispose()
        mGraphics = Nothing
    End Sub

We start by adding a class level object for the DX3DEngine we just made. Then the contructor for the GameEngine class receives a render target and creates an instance of the DX3DEngine object.

The Render procedure just begins and ends the rendering and then the class destructor disposes of the DX3DEngine object.

Liks I said, not really much there and it might not be apparent at this point why we needed a GameEngine, but trust me, this class will begin to have great importance and you will be glad for the seperation.

Well, we are almost there, only one more class to create and you'll be able to see the final results. With this last class we'll finally clean up that pesky error that has been haunting you from the start of this tutorial as well.

::Creating Main::

Add a new class to your solution and call it "Main". Main will only contain one procedure for now but it basicall gets your game running and keeps it running and then shuts it down when appropriate so that one function has quite a lot to do.

Main Class code

    'Description: The main entry point for the project. Sets up the game engine and 

contains the game loop
    Public Shared Sub Main()
        'Create a form to attach the DirectX device to
        Dim aMainForm As New 

Windows.Forms.Form
 
        'Create the Game engine
        Dim aGameEngine As New GameEngine(aMainForm)
 
        'Display the form
        aMainForm.Show()
 
        'Continue the game loop while the form is still running
        Do While aMainForm.IsDisposed = False
            'Give the computer a chance to process some other things
            Application.DoEvents()
 
            'Render the scene
            aGameEngine.Render()
        Loop
 
        'Destroy the Game Engine
        aGameEngine = Nothing
 
        'Destroy the Main form
        aMainForm = Nothing
    End Sub

In Main you can see we create a new form, that's where we will be rendering too with our device. Then we create a new GameEngine object and give it the form as the rendering target. Next we show the form and begin the game loop. Finally after the form has been disposed we clean up our objects.

But wait, we still have an error! Well to take care of the error we need to double-click on the "My Project" in the solution explorer and on the "Application" tab uncheck the "Enable application framework" checkbox. Now select Main as your startup object and the error that has been haunting our dreams since the beginning of this tutorial will finally go away.



Well, that's it you can now run the project and see a wonderfully rendered black background on a form. I know it's not a lot, but it's a very good base to start with and really lays the groundwork for a future 2D game.



I would suggest you play with clearing the device with different colors and seeing the behavior when you change to FullScreen mode and play with the buffer heights and widths while in that mode. Just note, when changing to full screen mode, you won't have a way of easily closing the application. You will have to Alt-Tab back to the projcect and stop the execution there. We'll be covering a tutorial on Keyboard input at a later point so that we can easily close it when in full screen mode and oh, I don'tk now maybe move some things around or something.

Hopefully that can keep you entertained long enough for me to finish another tutorial.

The source code for the tutorial is available here.

In closing, I've never done tutorials before so I welcome any and all feedback from layout to styling to whatever. I really want these tutorials to be instructional and motivational (as in, Wow! Managed DirectX doesn't look as intimidating as I thought, I think I could do that!), so please help me out if you have suggestions or advice for improving them. If I'm doing a great job, let me know that too, just a simple comment can be really motivating and are always appreciated.

Print | posted @ Monday, July 03, 2006 6:11 PM

Comments on this entry:

Gravatar # re: Managed DirectX Tutorial 01: Setting up DirectX or "Gee, I can make black form."
by George at 7/7/2006 10:23 AM

Thank you so much for commenting! The good news is that I have my next tutorial just about completed. I was just waiting to see if they were actually useful to anyone before I finished it up and posted it.

I will try and get it posted sometime tonight (I'm pacific time so depending on where you're at it could end up being early tomorrow morning)

...and it's on drawing your first sprite!

Thanks again for your comment, it really made my day.
Gravatar # re: Managed DirectX Tutorial 01: Setting up DirectX or "Gee, I can make black form."
by galantir at 7/22/2006 11:39 PM

Still have a little question about this tutorial.
When i try to set the device to a width of 1050 and height of 1600 it won't work.
This is however the resolution i play most of my games in. Is there a way to get that resolution?
Gravatar # re: Managed DirectX Tutorial 01: Setting up DirectX or "Gee, I can make black form."
by Alex at 8/25/2006 2:49 AM

Cheers, found this very useful as a student looking to get to know DirectX, hopefully to gain a better understanding of XNA when it comes out.
Gravatar # re: Managed DirectX Tutorial 01: Setting up DirectX or "Gee, I can make black form."
by George at 8/25/2006 5:35 AM


I'm glad it was a help. If I could find some time I would post the next three I have done in the series but I've been swamped by this crazy thing called "life" lately.

I am planning on hitting the ground running with XNA tutorials once it's released as well, so stop on by and we can trade tips and tricks as we both struggle through it.

Gravatar # re: Managed DirectX Tutorial 01: Setting up DirectX or "Gee, I can make black form."
by cometfish at 11/27/2007 5:08 PM

Hi,

Excellent tutorial thankyou! I have been searching for one for the past three days, and yours was the only decent, current one I could find :)

I do have one problem though... I get a 'GraphicsException' on the line:
myD3DDevice.Present()
in the Present function when the program exits.
I have just added another line to catch all exceptions and ignore this one, but I'd like to know why this is happening?
Any help appreciated :)

I'm moving on to the next tutorial now :)
Gravatar # re: Managed DirectX Tutorial 01: Setting up DirectX or "Gee, I can make black form."
by Darrell at 12/29/2007 10:46 PM

Hi great tutorial! as a newbie to direct x its great that you can get some results so quickly - however i did find that the section of the code for "discovering" the paramaters for the device was very offputting - simply because i didnt understand what the hell we were trying to ascertain and why! would have been nice to go into a little more detail here - but hey thats just me personally. Thanks great work!
Gravatar # re: Managed DirectX Tutorial 01: Setting up DirectX or "Gee, I can make black form."
by Nick at 2/1/2008 4:22 PM

When creating the device it says accessible New accepts this number of arguments.

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 6 and 4 and type the answer here:
 
Twitter