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
_GameTiles
List(Of GameTile)
'An instance of GameTime to track running game time
_GameTime
GameTime
'Two bitmaps and a boolean used to buffer drawing and rendering
_Buffer1
Bitmap(ClientSize.width, ClientSize.height)
_Buffer2
Bitmap(_Buffer1.Width, _Buffer1.Height)
_BufferFlag
Boolean
Sub
Form1_Load(sender
System.
Object
, e
System.EventArgs)
Handles
MyBase
.Load
'setup the form
Me
.DoubleBuffered =
True
.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D
'load some image assets to use for frames of animation
Dim
imageList
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
Integer
= 0
To
7
x
tile
GameTile
tile.FrameImages.AddRange(imageList)
tile.Location =
Point(12 + (x * tile.Bounds.Width), 12 + (y * tile.Bounds.Height))
_GameTiles.Add(tile)
Next
'set the game time to 30 fps (1000ms / 30frames)
Timer1.Interval = 33
'start the game loop
Timer1.Start()
End
'Use a stopwatch to track the execution time
_ElapsedTime
Stopwatch
Timer1_Tick(sender
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.
Each
In
If
MouseButtons = Windows.Forms.MouseButtons.Left
Then
tile.Bounds.Contains(PointToClient(MousePosition))
tile.OnInput(_GameTime)
'Run a loop to draw each item after determining which
'buffer to draw on this frame
gfx
Graphics
gfx = Graphics.FromImage(_Buffer1)
Else
gfx = Graphics.FromImage(_Buffer2)
gfx.Clear(BackColor)
tile.OnDraw(_GameTime, gfx)
'Cleanup and swap buffers
gfx.Dispose()
_BufferFlag =
Not
'Show the drawn scene
Invalidate()
Protected
Overrides
OnPaint(e
System.Windows.Forms.PaintEventArgs)
.OnPaint(e)
'Draw the approprite render buffer
e.Graphics.DrawImageUnscaled(_Buffer2, Point.Empty)
e.Graphics.DrawImageUnscaled(_Buffer1, Point.Empty)
Property
Location
Point
FrameImages
'this is the images per second of the animation
FrameRate
Double
= 8.0
'this is the total time to animate after recieving a click
_AnimationTime
ReadOnly
Bounds
Rectangle
Get
Return
Rectangle(Location, FrameImages(CurrentFrameIndex).Size)
_FrameIndex
CurrentFrameIndex
CInt
(Math.Floor(_FrameIndex))
OnInput(gameTime
GameTime)
'set the remaining animation time to 3 seconds when clicked
_AnimationTime = 3.0
OnDraw(gameTime
GameTime, gfx
Graphics)
'draw the current frame at its current location
gfx.DrawImageUnscaled(FrameImages(CurrentFrameIndex), Location)
'if there is remaining animation time, then animate
_AnimationTime > 0
_FrameIndex += gameTime.LastFrame * FrameRate
CurrentFrameIndex = FrameImages.Count
_FrameIndex = 0.0
_AnimationTime -= gameTime.LastFrame
'GameTime can be a simple structure or class which just tracks executed
'game time based on what the game loop tells it
Structure
ElapsedTime
TimeSpan
LastFrame
Elapse(milliseconds
Long
)
ElapsedTime += TimeSpan.FromMilliseconds(milliseconds)
LastFrame = milliseconds / 1000
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! =)
Technet Wiki really needs a like button... Nice article Reed. :-)
Paul Ishak edited Original. Comment: Attempt to fix code formatting(was only showing as plain text)
Revision: edited tags
Congratulations on winning the May TechNet Guru contest! blogs.technet.com/.../technet-guru-awards-may-2013.aspx
Great article
Congratulations on being featured on the home page of TechNet Wiki! social.technet.microsoft.com/.../default.aspx
Thank you everyone, I hope to be able to continue to provide quality articles (if I haven't set my own bar too high!).
We'll announce your win in a few blog posts. First, here was the compilation announcement on Wiki Ninjas: blogs.technet.com/.../technet-guru-awards-may-2013.aspx
Here's my announcement on MSDN Blogs: blogs.msdn.com/.../vb-guru-how-to-create-video-games-in-vb-net-windows-forms.aspx
Naomi N edited Revision 8. Comment: The last user is a spammer - better to revert