Home | Site Map
Sunlight

DirectX.NET VB.NET Test

Introduction

This chapter describes a test application for DirectX .NET in Visual Basic.NET. It uses many of the DirectX .NET classes to implement an application very similar to that built in the DirectX tutorial.

Structure

The application was initially built using the Visual Studio form editor. A new form, called Form1, was created, with its border style set to 'fixed single' and its title set to "DXTest".

Now we add the namespaces which we will use in this program at the top:

Imports System
Imports System.IO
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms

Imports Sunlight.DirectX.Graphics
Imports Sunlight.DirectX.Input
Imports Sunlight.DirectX.SoundMusic

The form base class was then changed to Sunlight.IdleForm, to allow us to use the idle-time processing.

We add a number of fields, to support the graphics, audio and input systems, which you can see in the code.

Initialisation

The first thing that occurs in a VB.NET program is that a static Main method, in a module (rather than a class), is called. This is simply a case of creating the form and letting it run:

Module DXTestModule
    ' The main entry point for the application.
    Sub Main()
        Dim form As DXTest.Form1 = New DXTest.Form1()
        form.Run()
    End Sub
End Module

DirectX initialisation should be performed in the Init event fired by IdleForm. In VB.NET, events are handled by specifying the event on the first line of the function:

Protected Sub OnInit(ByVal sender As Object, ByVal e As EventArgs) 
	Handles MyBase.Init

The bulk of the initialisation takes place in the OnInit method. First, the Device object is built (but not created; this will happen at the end of OnInit). This object is built first to allow the DirectInput object to reference it.

    ' Initialise the DirectX Graphics device.
    device = New Device(d3d)
    device.ParentWindow = Me
    device.Windowed = True

 Now, we build the DirectInput object, together with its associated action map.

    ' Initialise the DirectInput object, including the action map.
    di.ActionMapName = "DXTest"
    di.AppID = New Guid("{CA761232-ED42-11CE-BACD-00AA0057B223}")
    di.Genre = Genres.ARCADE_SIDE2SIDE
    di.Actions.Add(New ActionMap.Entry(0, Actions.AXIS_ARCADES_LATERAL, "Left/Right"))
    di.Actions.Add(New ActionMap.Entry(1, Actions.KEYBOARD_LEFT, "Left"))
    di.Actions.Add(New ActionMap.Entry(2, Actions.KEYBOARD_RIGHT, "Right"))
    di.Actions.Add(New ActionMap.Entry(3, Actions.KEYBOARD_ESCAPE, "Quit"))
    di.Actions.Add(New ActionMap.Entry(4, Actions.KEYBOARD_P, "Pause"))
    di.Actions.Add(New ActionMap.Entry(5, Actions.KEYBOARD_F1, "Help"))
    di.Actions.Add(New ActionMap.Entry(6, Actions.KEYBOARD_S, "Full Screen On/Off"))
    di.ParentWindow = Me
    di.Direct3DDevice = device

The GUID was generated using GUIDGEN, part of the Platform SDK. You should use a different one for your applications. We have defined the genre as a side-to-side arcade game, which accurately describes the set of actions available for this application. Next, the action map is built up.

The next step is to initialise the music system. First, we must initialise a DirectMusic object:

    ' Initialise the DirectMusic object.
    dm.ParentWindow = Me
    dm.Create()

Now, we load a MIDI file in as the background music. We trap the Finished event so that we can make the music repeat.

    ' Load the background music. This has an event handler to allow it to repeat.
    soundMusic.DirectMusicObject = dm
    soundMusic.Filename = "passport.mid"

We now want to build the sprite objects. First, we need a SpriteManager:

    ' Initialise the sprite manager.
    manager.DeviceObject = device

Next, we need to initialise the textures the sprites will reference. The textures will be automatically loaded when the device is created.

    ' Initialise the textures.
    texBackground.DeviceObject = device
    texBackground.Filename = "a.png"
    texSprites.DeviceObject = device
    texSprites.Filename = "sprites.png"

Now we can build the sprites.

    ' Build the sprites.
    spriteBackground = New Sprite(manager, New Rectangle(0, 0, 640, 480), 
	texBackground, New Point(0, 0))
    spriteFixed = New Sprite(manager, New Rectangle(288, 208, 64, 64), 
	texSprites, New Point(128, 0))
    spriteMoving = New Sprite(manager, New Rectangle(288, 100, 64, 64), 
	texSprites, New Point(0, 0))
    spritePlayer = New Sprite(manager, New Rectangle(288, 150, 64, 64), 
	texSprites, New Point(64, 0))

Finally, we can create the Device object.

    device.Create()

We will now test the DirectInput configuration UI, just as before.

    di.Configure()

Finally, before the application begins, we will start the music playing:

    soundMusic.Play()

Music Handling

When the music finishes, we simply want to repeat the same track over again. Note that again we specify the event which the Sub is to handle on the first line.

' Called when the music has reached the end.
Private Sub OnMusicFinished(ByVal sender As Object, ByVal e As EventArgs) 
	Handles soundMusic.Finished
    ' just repeat
    soundMusic.Play()
End Sub

Input Handling

Input handling is almost identical to that in the DirectX tutorial, and is very simple.

' Called when an input event occurs.
Sub OnInputAction(ByVal sender As DirectInput, ByVal e As DirectInputEventArgs) 
	Handles di.Action
    Select Case (e.ID)
        Case 0
            If (e.AxisPosition < -20) Then
                bLeft = True
                bRight = False
            ElseIf (e.AxisPosition > 20) Then
                bRight = True
                bLeft = False
            Else
                bLeft = False
                bRight = False
            End If

        Case 1
            bLeft = e.ButtonPressed

        Case 2
            bRight = e.ButtonPressed

        Case 3
            If (e.ButtonPressed) Then bQuit = True

        Case 4
            If (e.ButtonPressed) Then bPaused = Not bPaused

        Case 5
            If (e.ButtonPressed) Then bShowHelp = True

        Case 6
            If (e.ButtonPressed) Then bModeSwitch = True
    End Select
End Sub

Drawing and Motion

The real meat of DXTest is in OnIdle, where all drawing occurs, and the program reacts to inputs.

' Called when the application is idle (i.e. no messages are being processed).
' Contains the main application logic.
Protected Sub OnIdle(ByVal sender As Object, ByVal e As EventArgs) 
	Handles MyBase.Idle

The first thing to do is to check the input for any changes.

    ' Check for updated inputs and dispatch input events.
    di.Check()

We can now handle the various inputs. Closing the application is simply a case of closing the form:

    If (bQuit) Then
        Close()
        Return
    End If

Changing modes is equally simple: changing the Windowed flag indicates that a mode switch should occur, and Device.Reset performs the switch.

    If (bModeSwitch) Then
        device.Windowed = Not device.Windowed
        device.Reset()
        bModeSwitch = False
    End If

Help is performed by displaying the DirectInput configuration UI in display-only mode.

    If (bShowHelp) Then
        bShowHelp = False
        di.Display()
    End If

The sprites are moved around if the device is not paused: one by the input, one automatically.

    If (Not bPaused) Then
        ' Move a sprite around with the input.
        If (bLeft) Then xPlayer -= 1
        If (bRight) Then xPlayer += 1
        spritePlayer.Left = 288 + xPlayer

        ' Move a sprite around automatically.
        xAutomaticSprite += 1
        If (xAutomaticSprite > 100) Then xAutomaticSprite = -100

        spriteMoving.Left = 288 + xAutomaticSprite
    End If

Now the sprites are drawn in the appropriate order (back to front):

    spriteBackground.Draw()
    spriteFixed.Draw()
    spriteMoving.Draw()
    spritePlayer.Draw()

Finally, the actual drawing is performed by SpriteManager.FinishDraw. A scene is first begun, the drawing occurs, then the scene is ended and a flip is performed to transfer the contents of the back buffer to the display.

    device.BeginScene()
    manager.FinishDraw()
    device.EndScene()
    device.Flip()
End Sub

Building the Application

If you are using Visual Studio, you should be able to load the project provided and build it. You may have to add a reference to your compiled Sunlight.DirectX.dll class library.

If you are building from the command line, enter the following command line. Note that the assemblies used by the project must all be listed on the command line:

vbc /out:DXTest.exe /r:Sunlight.DirectX.dll /r:System.Drawing.dll /r:System.Windows.Forms.dll /r:System.dll *.vb

Make sure the media files are in the directory from which you run the resulting assembly.

Conclusion

That's it. You should be able to run this against the assembly formed from the C++ .NET source code presented in the first few chapters, and see a working DirectX application - with graphics, sound and input - in under 200 lines of written code.