Share via


How to Create Video Games in VB.Net (Windows Forms)

How to Create Video Games in VB.Net (Windows Forms)

I realize that this is a very popular subject, especially amongst budding developers. The drive to create games may be the reason you started working with Visual Basic in the first place. After stepping through a few samples and getting the hang of working with user controls on a form, it may be very tempting to start to throw some PictureBoxes and a Timer on a Form and start to implement some game logic. Seems easy enough, right?

To some extent, this is a true statement. You could start to do this and it would be easy enough… at first. But when you begin to try to calculate collision and animate or rotate your “sprites”, you may start to run into some difficulty. And attempts to circumvent the problems often lead to even worse issues. This can cause an endless spiral of misery which could leave you thinking VB just isn’t meant to make games! ;)

The initial problem that most people face is the desire to use a PictureBox (or any other control) as the logical “Sprite” container for the elements in the game. It makes sense since the control appears to provide a lot of the required functionality already and it’s easy to extend it with more properties as needed.

The issue though is that Windows Forms Controls are designed to be drawn statically – that is, they aren’t meant to move around in real-time. You can of course move them at run-time, but this is normally an on-demand operation (something which occurs because the user just took an action like clicking a button or menu item). Attempting to move controls in real-time puts a heavy strain on your application and can cause poor performance quickly.

There’s also the issue that a control is painted according to its own logic, so you can’t just take any old control and “rotate” it without modifying the logic which draws the control (at some level).

The other thing that Windows Forms leads you right into is using Events. So it’s natural to think to implement user input by handling key and mouse events on the PictureBox or the containing Form. But even though Windows Forms are designed to rely heavily on the Event chain, they are expecting the application to be idle most of the time, doing its work in fits and bursts. This kind of application works well even with many events and handlers.

But a game is a single long-running loop. Your Windows Forms application is technically a pre-specified Form instance started in a long-running message loop, but then you interact with the loop through the Event chain.

For the best performance, a .Net GameEngine should actually do away with the main Form and use the program’s main loop to execute the game loop functionality. But it can be acceptable to maintain the Form and a Control or Component or two and implement a GameEngine in componentized form.

However, this is where the use of controls stops. While there may be a component to house the “GameEngine” related functionality, and a “RenderCanvas” CustomControl to render the game engine display, all of the actual game elements would be class instances handled by the GameEngine component which are not controls of any kind.

Your “Sprite” class (we can debate terminology as technically a sprite is just an image resource, but for this discussion “sprite” is a game object of some sort with image and movement and collision and all) is its own custom “game object” class that you define to hold values such as location, speed, bounds, image and/or animation. The GameEngine is responsible for updating and drawing each game object once each game-loop-iteration and the RenderCanvas is responsible for rendering the last drawn frame.

Here is an example from a thread on the MSDN forums. This very simple example uses a Timer component as the “game engine” and the Form serves as the “render canvas”.

Option Strict On
  
Public Class  Form1
    'One timer controls the entire game loop
    Private WithEvents  Timer1 As  New Timer
  
    'A list of the game tile objects used by the game
    Private _GameTiles As New  List(Of GameTile)
    'An instance of GameTime to track running game time
    Private _GameTime As New  GameTime
  
    'Two bitmaps and a boolean used to buffer drawing and rendering
    Private _Buffer1 As New  Bitmap(ClientSize.width, ClientSize.height)
    Private _Buffer2 As New  Bitmap(_Buffer1.Width, _Buffer1.Height)
    Private _BufferFlag As Boolean
  
    Private Sub  Form1_Load(sender As  System.Object, e As  System.EventArgs) Handles MyBase.Load
        'setup the form
        Me.DoubleBuffered = True
        Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D
        'load some image assets to use for frames of animation
        Dim imageList As New  List(Of Image)
        imageList.Add(SystemIcons.Application.ToBitmap)
        imageList.Add(SystemIcons.Error.ToBitmap)
        imageList.Add(SystemIcons.Exclamation.ToBitmap)
        imageList.Add(SystemIcons.Information.ToBitmap)
        imageList.Add(SystemIcons.Question.ToBitmap)
        'create a grid of tiles
        For y As Integer  = 0 To  7
            For x As Integer  = 0 To  7
                Dim tile As New  GameTile
                tile.FrameImages.AddRange(imageList)
                tile.Location = New  Point(12 + (x * tile.Bounds.Width), 12 + (y * tile.Bounds.Height))
                _GameTiles.Add(tile)
            Next
        Next
        'set the game time to 30 fps (1000ms / 30frames)
        Timer1.Interval = 33
        'start the game loop
        Timer1.Start()
    End Sub
  
    'Use a stopwatch to track the execution time
    Private _ElapsedTime As New  Stopwatch
    Private Sub  Timer1_Tick(sender As Object, e As  System.EventArgs) Handles Timer1.Tick
        _ElapsedTime.Stop()
        'Record they time since the last loop iteration
        _GameTime.Elapse(_ElapsedTime.ElapsedMilliseconds)
        'Reset the stopwatch to 0 and start tracking again
        _ElapsedTime.Restart()
        'Run a loop to check input for each item.
        For Each  tile In  _GameTiles
            If MouseButtons = Windows.Forms.MouseButtons.Left  Then
                If tile.Bounds.Contains(PointToClient(MousePosition))  Then
                    tile.OnInput(_GameTime)
                End If
            End If
        Next
        'Run a loop to draw each item after determining which
        'buffer to draw on this frame
        Dim gfx As Graphics
        If _BufferFlag Then
            gfx = Graphics.FromImage(_Buffer1)
        Else
            gfx = Graphics.FromImage(_Buffer2)
        End If
        gfx.Clear(BackColor)
        For Each  tile In  _GameTiles
            tile.OnDraw(_GameTime, gfx)
        Next
        'Cleanup and swap buffers
        gfx.Dispose()
        _BufferFlag = Not  _BufferFlag
        'Show the drawn scene
        Invalidate()
    End Sub
  
    Protected Overrides  Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        'Draw the approprite render buffer
        If _BufferFlag Then
            e.Graphics.DrawImageUnscaled(_Buffer2, Point.Empty)
        Else
            e.Graphics.DrawImageUnscaled(_Buffer1, Point.Empty)
        End If
    End Sub
End Class
  
Public Class  GameTile
    Public Property  Location As  Point
    Public Property  FrameImages As  New List(Of Image)
    'this is the images per second of the animation
    Public Property  FrameRate As  Double = 8.0
    'this is the total time to animate after recieving a click
    Private _AnimationTime As Double
  
    Public ReadOnly  Property Bounds As Rectangle
        Get
            Return New  Rectangle(Location, FrameImages(CurrentFrameIndex).Size)
        End Get
    End Property
  
    Private _FrameIndex As Double
    Public ReadOnly  Property CurrentFrameIndex As Integer
        Get
            Return CInt(Math.Floor(_FrameIndex))
        End Get
    End Property
  
    Public Sub  OnInput(gameTime As  GameTime)
        'set the remaining animation time to 3 seconds when clicked
        _AnimationTime = 3.0
    End Sub
  
    Public Sub  OnDraw(gameTime As  GameTime, gfx As  Graphics)
        'draw the current frame at its current location
        gfx.DrawImageUnscaled(FrameImages(CurrentFrameIndex), Location)
        'if there is remaining animation time, then animate
        If _AnimationTime > 0 Then
            _FrameIndex += gameTime.LastFrame * FrameRate
            If CurrentFrameIndex = FrameImages.Count Then _FrameIndex = 0.0
            _AnimationTime -= gameTime.LastFrame
        Else
            _FrameIndex = 0.0
        End If
    End Sub
End Class
  
'GameTime can be a simple structure or class which just tracks executed
'game time based on what the game loop tells it
Public Structure  GameTime
    Public ElapsedTime As TimeSpan
    Public LastFrame As Double
  
    Public Sub  Elapse(milliseconds As Long)
        ElapsedTime += TimeSpan.FromMilliseconds(milliseconds)
        LastFrame = milliseconds / 1000
    End Sub
End Structure

Notice that the code only defines two classes other than the Form. This example defines a single “sprite” or “game object” called GameTile, and a structure called GameTime which is used to track the game engine execution time (both total time and last frame time, that is, last loop iteration time) – an important set of values in many calculations.

The GameTile class is overly-simple and just has an OnInput and OnDraw logic processing routine for the engine to call. In a more robust model, there would be additional logic processing methods which would get called at various appropriate points throughout each execution of the game engine loop.

The GameTile has very simple logic which just executes a 3 second “animation” (sequence of images) played at the frames-per-second specified by FrameRate whenever the tile is clicked.

The game engine loop itself is implemented in the Timer1_Tick event handler. As previously noted, this is not ideal as the event chain is less efficient for our purposes than running a background thread would be, but the timer keeps the example simple. Regardless of where it is run, the logic executed by this Timer1_Tick event handler lays out the general structure of the logic which needs to occur once each game loop iteration.

First, we update the time based on how long the last frame took to execute. You might do this at the end of each iteration, rather than at the beginning, but this implementation does it first because it makes the code simple to follow.

Next we update input for each game object (GameTile). Since the example is running directly off of the Form’s thread we can just cheat on input and read it in real time from the Form. A more robust game loop would maintain an Input object which was updated once per frame before beginning on game objects. So the code checks each object to see if the mouse is being held down on it, and calls the OnInput method for that object if so.

Game objects should have their input updated first, then their logic processed, and finally their positions drawn. It is important to perform separate loops for each operation so that all game objects work together in a predictable manner. This example does not use an OnUpdate method, but typically, OnInput, OnUpdate, and OnDraw are the minimum logic processing methods for a game object.

Now that all of the GameTile objects are updated, they can have their current state drawn. The example does implement buffered drawing even though this simple implementation doesn’t really require it. But it is not very complex and it makes it much more clear how to transition drawing into multiple threads.

By implementing a buffered drawing (do not confuse with DoubleBuffered property of Form – that just makes GDI behave for real-time drawing) we ensure that the image being processing by the Form thread is never the same as the image being processed by the background game engine loop. The Form renders one buffer image and the game loop draws to the other, and then flips buffers as the last operation on each frame. After the buffer has been flipped, a call can be sent off to invalidate the Form (or render control) and the game loop can continue processing and drawing the next frame (on the alternate buffer).

Once the code has selected the correct buffer for drawing this frame, it proceeds to create the graphics object which will be passed to every game object allowing it to draw itself to the buffer. When this process is complete, the graphics can be freed, the buffer can be flipped, and the render control can be informed that it needs to update.

Again, and I can’t stress enough, this is only a most basic implementation – a robust game engine has a lot more work to do. But this example should provide a simple overview of the primary concepts involved with creating a game engine.

XNA

The next most important thing to be aware of when it comes to creating games in VB.Net is that Microsoft has an official platform just for Games and Interactive Software, called XNA Framework. As of the latest refresh you can now use VB with XNA Game Studio. Although XNA can seem quite daunting at first, if you understand the basic concepts lain out above, then you are already well on your way to using it. XNA follows a similar pattern to what I’ve described here, and there are lots of examples and tutorials to get you started. There is just a lot more to it since XNA supports everything - all 2D, 3D, audio, game-pad input, multiplayer networking, Xbox Live access, etc.

Games created in XNA can also easily be written to port from Windows to Xbox to WindowsPhone7. And you can easily become a publisher and actually put your game on Xbox Live or Windows Marketplace and collect royalties for its purchases.

Now, for a fairly simple, very “classic” style 2D video game, XNA could be considered overkill. And what you can accomplish with GDI+ and some creative sound and input hooks might provide everything that your game design requires. But just keep in mind that broad support for game development is available under the XNA platform and it is incredibly powerful once you get the hang of using it (which really isn’t as bad as it first may seem if you just stick with it!).

Unity

I always feel obligated to mention this non-Microsoft-based solution because it is just so dang powerful and cool and wonderful and free (to play with anyway!). Unity3D is a complete 3D development environment; like Visual Studio only the “form designer” is a 3D world space and the toolbox is full of game objects! The AI can be developed through script plugins in multiple languages including C# and Java. So while it’s not exactly a VB solution, I still bring it up because the C# you would need to know is pretty easy to convert from VB and the Unity engine itself is truly remarkable.

In many ways what Unity does is very much like what XNA does. The main difference is that Unity goes so far as to define what a game engine is and then allows you to easily extend it, whereas XNA only provides all of the interfaces necessary for you to create your own game engine of some kind. This is what allows Unity to provide a rich editor full of drag-and-drop and property-grid-setting features.

GdiGaming API

Finally, if you still have your heart set on making a quick little game directly in VB.Net (and why shouldn’t you?! They do make such fun projects!), then you may wish to check out the GdiGaming API over on CodePlex.

This is a project I’m working on as I have time, which is meant to give a robust example of creating game engine in VB.Net using GDI as well as provide a compiled runtime which can be used as-is to quickly make little games based around the engine.

While there may still be a few bugs to work out, the API works generally as intended and is quite powerful for what it does and how it is designed.  The project is a pretty good start on a full-scale game engine based around the kind of example shown above. It is open-source (as CodePlex implies) and I’ve tried to provide fairly rich documentation and sample code. There’s still plenty to do (always a work in progress I guess!) but I believe there is enough there already for it to be of additional help if you are interested in reading more.

Conclusion

I hope that somewhere in all of this there is some useful information for those of you starting out to write games in .Net. The horizons are currently broad, and the roads become more and more well-traveled every day. The amount of information available is tremendous and I would encourage you to play with all of the technologies mentioned in this article and read all of their help files. Even if you don’t download anything yet, just go to the XNA and Untiy websites, navigate to their intro tutorials and read them. Take note of the terminology and get a general feel for what the code appears to do, even if you can’t read all of it.

The next great game is just waiting to be written, and with effort and determination in no short supply you could be the one to write it! =)

See Also An important place to find a huge amount of Visual Basic related articles is the TechNet Wiki itself. The best entry point is Visual Basic Resources on the TechNet Wiki