
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.
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.
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()
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 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
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
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.
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.