EDN Admin
Well-known member
How to Create Video Games in VB.Net
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 recent thread. This very simple example uses a Timer component as the âgame engineâ and the Form serves as the ârender canvasâ.
<div style="color:black; background-color:white
<pre><span style="color:blue Option Strict <span style="color:blue On
<span style="color:blue Public <span style="color:blue Class Form1
<span style="color:green One timer controls the entire game loop
<span style="color:blue Private <span style="color:blue WithEvents Timer1 <span style="color:blue As <span style="color:blue New Timer
<span style="color:green A list of the game tile objects used by the game
<span style="color:blue Private _GameTiles <span style="color:blue As <span style="color:blue New List(Of GameTile)
<span style="color:green An instance of GameTime to track running game time
<span style="color:blue Private _GameTime <span style="color:blue As <span style="color:blue New GameTime
<span style="color:green Two bitmaps and a boolean used to buffer drawing and rendering
<span style="color:blue Private _Buffer1 <span style="color:blue As <span style="color:blue New Bitmap(ClientSize.width, ClientSize.height)
<span style="color:blue Private _Buffer2 <span style="color:blue As <span style="color:blue New Bitmap(_Buffer1.Width, _Buffer1.Height)
<span style="color:blue Private _BufferFlag <span style="color:blue As <span style="color:blue Boolean
<span style="color:blue Private <span style="color:blue Sub Form1_Load(sender <span style="color:blue As System.Object, e <span style="color:blue As System.EventArgs) <span style="color:blue Handles <span style="color:blue MyBase.Load
<span style="color:green setup the form
<span style="color:blue Me.DoubleBuffered = <span style="color:blue True
<span style="color:blue Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D
<span style="color:green load some image assets to use for frames of animation
<span style="color:blue Dim imageList <span style="color:blue As <span style="color:blue New List(Of Image)
imageList.Add(SystemIcons.Application.ToBitmap)
imageList.Add(SystemIcons.<span style="color:blue Error.ToBitmap)
imageList.Add(SystemIcons.Exclamation.ToBitmap)
imageList.Add(SystemIcons.Information.ToBitmap)
imageList.Add(SystemIcons.Question.ToBitmap)
<span style="color:green create a grid of tiles
<span style="color:blue For y <span style="color:blue As <span style="color:blue Integer = 0 <span style="color:blue To 7
<span style="color:blue For x <span style="color:blue As <span style="color:blue Integer = 0 <span style="color:blue To 7
<span style="color:blue Dim tile <span style="color:blue As <span style="color:blue New GameTile
tile.FrameImages.AddRange(imageList)
tile.Location = <span style="color:blue New Point(12 + (x * tile.Bounds.Width), 12 + (y * tile.Bounds.Height))
_GameTiles.Add(tile)
<span style="color:blue Next
<span style="color:blue Next
<span style="color:green set the game time to 30 fps (1000ms / 30frames)
Timer1.Interval = 33
<span style="color:green start the game loop
Timer1.Start()
<span style="color:blue End <span style="color:blue Sub
<span style="color:green Use a stopwatch to track the execution time
<span style="color:blue Private _ElapsedTime <span style="color:blue As <span style="color:blue New Stopwatch
<span style="color:blue Private <span style="color:blue Sub Timer1_Tick(sender <span style="color:blue As <span style="color:blue Object, e <span style="color:blue As System.EventArgs) <span style="color:blue Handles Timer1.Tick
_ElapsedTime.<span style="color:blue Stop()
<span style="color:green Record they time since the last loop iteration
_GameTime.Elapse(_ElapsedTime.ElapsedMilliseconds)
<span style="color:green Reset the stopwatch to 0 and start tracking again
_ElapsedTime.Restart()
<span style="color:green Run a loop to check input for each item.
<span style="color:blue For <span style="color:blue Each tile <span style="color:blue In _GameTiles
<span style="color:blue If MouseButtons = Windows.Forms.MouseButtons.Left <span style="color:blue Then
<span style="color:blue If tile.Bounds.Contains(PointToClient(MousePosition)) <span style="color:blue Then
tile.OnInput(_GameTime)
<span style="color:blue End <span style="color:blue If
<span style="color:blue End <span style="color:blue If
<span style="color:blue Next
<span style="color:green Run a loop to draw each item after determining which
<span style="color:green buffer to draw on this frame
<span style="color:blue Dim gfx <span style="color:blue As Graphics
<span style="color:blue If _BufferFlag <span style="color:blue Then
gfx = Graphics.FromImage(_Buffer1)
<span style="color:blue Else
gfx = Graphics.FromImage(_Buffer2)
<span style="color:blue End <span style="color:blue If
gfx.Clear(BackColor)
<span style="color:blue For <span style="color:blue Each tile <span style="color:blue In _GameTiles
tile.OnDraw(_GameTime, gfx)
<span style="color:blue Next
<span style="color:green Cleanup and swap buffers
gfx.Dispose()
_BufferFlag = <span style="color:blue Not _BufferFlag
<span style="color:green Show the drawn scene
Invalidate()
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue Protected <span style="color:blue Overrides <span style="color:blue Sub OnPaint(e <span style="color:blue As System.Windows.Forms.PaintEventArgs)
<span style="color:blue MyBase.OnPaint(e)
<span style="color:green Draw the approprite render buffer
<span style="color:blue If _BufferFlag <span style="color:blue Then
e.Graphics.DrawImageUnscaled(_Buffer2, Point.Empty)
<span style="color:blue Else
e.Graphics.DrawImageUnscaled(_Buffer1, Point.Empty)
<span style="color:blue End <span style="color:blue If
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue End <span style="color:blue Class
<span style="color:blue Public <span style="color:blue Class GameTile
<span style="color:blue Public <span style="color:blue Property Location <span style="color:blue As Point
<span style="color:blue Public <span style="color:blue Property FrameImages <span style="color:blue As <span style="color:blue New List(Of Image)
<span style="color:green this is the images per second of the animation
<span style="color:blue Public <span style="color:blue Property FrameRate <span style="color:blue As <span style="color:blue Double = 8.0
<span style="color:green this is the total time to animate after recieving a click
<span style="color:blue Private _AnimationTime <span style="color:blue As <span style="color:blue Double
<span style="color:blue Public <span style="color:blue ReadOnly <span style="color:blue Property Bounds <span style="color:blue As Rectangle
<span style="color:blue Get
<span style="color:blue Return <span style="color:blue New Rectangle(Location, FrameImages(CurrentFrameIndex).Size)
<span style="color:blue End <span style="color:blue Get
<span style="color:blue End <span style="color:blue Property
<span style="color:blue Private _FrameIndex <span style="color:blue As <span style="color:blue Double
<span style="color:blue Public <span style="color:blue ReadOnly <span style="color:blue Property CurrentFrameIndex <span style="color:blue As <span style="color:blue Integer
<span style="color:blue Get
<span style="color:blue Return <span style="color:blue CInt(Math.Floor(_FrameIndex))
<span style="color:blue End <span style="color:blue Get
<span style="color:blue End <span style="color:blue Property
<span style="color:blue Public <span style="color:blue Sub OnInput(gameTime <span style="color:blue As GameTime)
<span style="color:green set the remaining animation time to 3 seconds when clicked
_AnimationTime = 3.0
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue Public <span style="color:blue Sub OnDraw(gameTime <span style="color:blue As GameTime, gfx <span style="color:blue As Graphics)
<span style="color:green draw the current frame at its current location
gfx.DrawImageUnscaled(FrameImages(CurrentFrameIndex), Location)
<span style="color:green if there is remaining animation time, then animate
<span style="color:blue If _AnimationTime > 0 <span style="color:blue Then
_FrameIndex += gameTime.LastFrame * FrameRate
<span style="color:blue If CurrentFrameIndex = FrameImages.<span style="color:blue Count <span style="color:blue Then _FrameIndex = 0.0
_AnimationTime -= gameTime.LastFrame
<span style="color:blue Else
_FrameIndex = 0.0
<span style="color:blue End <span style="color:blue If
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue End <span style="color:blue Class
<span style="color:green GameTime can be a simple structure or class which just tracks executed
<span style="color:green game time based on what the game loop tells it
<span style="color:blue Public <span style="color:blue Structure GameTime
<span style="color:blue Public ElapsedTime <span style="color:blue As TimeSpan
<span style="color:blue Public LastFrame <span style="color:blue As <span style="color:blue Double
<span style="color:blue Public <span style="color:blue Sub Elapse(milliseconds <span style="color:blue As <span style="color:blue Long)
ElapsedTime += TimeSpan.FromMilliseconds(milliseconds)
LastFrame = milliseconds / 1000
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue End <span style="color:blue Structure
[/code]
<br/>
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
http://create.msdn.com/en-US/" target="_blank 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!).
http://unity3d.com/ 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
http://gdigaming.codeplex.com" target="_blank 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 are 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 main error I have is a poor design of the AssetManager and I just have to recode it â not a big deal
but it changes a lot of code so Iâm waiting until I know I have time to fix it right. The other minor issues are already corrected and pending this big one for a new publish.
Anyway, the project is still 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! =)
<
Reed Kimble - "When you do things right, people wont be sure youve done anything at all"<br/>
View the full article
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 recent thread. This very simple example uses a Timer component as the âgame engineâ and the Form serves as the ârender canvasâ.
<div style="color:black; background-color:white
<pre><span style="color:blue Option Strict <span style="color:blue On
<span style="color:blue Public <span style="color:blue Class Form1
<span style="color:green One timer controls the entire game loop
<span style="color:blue Private <span style="color:blue WithEvents Timer1 <span style="color:blue As <span style="color:blue New Timer
<span style="color:green A list of the game tile objects used by the game
<span style="color:blue Private _GameTiles <span style="color:blue As <span style="color:blue New List(Of GameTile)
<span style="color:green An instance of GameTime to track running game time
<span style="color:blue Private _GameTime <span style="color:blue As <span style="color:blue New GameTime
<span style="color:green Two bitmaps and a boolean used to buffer drawing and rendering
<span style="color:blue Private _Buffer1 <span style="color:blue As <span style="color:blue New Bitmap(ClientSize.width, ClientSize.height)
<span style="color:blue Private _Buffer2 <span style="color:blue As <span style="color:blue New Bitmap(_Buffer1.Width, _Buffer1.Height)
<span style="color:blue Private _BufferFlag <span style="color:blue As <span style="color:blue Boolean
<span style="color:blue Private <span style="color:blue Sub Form1_Load(sender <span style="color:blue As System.Object, e <span style="color:blue As System.EventArgs) <span style="color:blue Handles <span style="color:blue MyBase.Load
<span style="color:green setup the form
<span style="color:blue Me.DoubleBuffered = <span style="color:blue True
<span style="color:blue Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D
<span style="color:green load some image assets to use for frames of animation
<span style="color:blue Dim imageList <span style="color:blue As <span style="color:blue New List(Of Image)
imageList.Add(SystemIcons.Application.ToBitmap)
imageList.Add(SystemIcons.<span style="color:blue Error.ToBitmap)
imageList.Add(SystemIcons.Exclamation.ToBitmap)
imageList.Add(SystemIcons.Information.ToBitmap)
imageList.Add(SystemIcons.Question.ToBitmap)
<span style="color:green create a grid of tiles
<span style="color:blue For y <span style="color:blue As <span style="color:blue Integer = 0 <span style="color:blue To 7
<span style="color:blue For x <span style="color:blue As <span style="color:blue Integer = 0 <span style="color:blue To 7
<span style="color:blue Dim tile <span style="color:blue As <span style="color:blue New GameTile
tile.FrameImages.AddRange(imageList)
tile.Location = <span style="color:blue New Point(12 + (x * tile.Bounds.Width), 12 + (y * tile.Bounds.Height))
_GameTiles.Add(tile)
<span style="color:blue Next
<span style="color:blue Next
<span style="color:green set the game time to 30 fps (1000ms / 30frames)
Timer1.Interval = 33
<span style="color:green start the game loop
Timer1.Start()
<span style="color:blue End <span style="color:blue Sub
<span style="color:green Use a stopwatch to track the execution time
<span style="color:blue Private _ElapsedTime <span style="color:blue As <span style="color:blue New Stopwatch
<span style="color:blue Private <span style="color:blue Sub Timer1_Tick(sender <span style="color:blue As <span style="color:blue Object, e <span style="color:blue As System.EventArgs) <span style="color:blue Handles Timer1.Tick
_ElapsedTime.<span style="color:blue Stop()
<span style="color:green Record they time since the last loop iteration
_GameTime.Elapse(_ElapsedTime.ElapsedMilliseconds)
<span style="color:green Reset the stopwatch to 0 and start tracking again
_ElapsedTime.Restart()
<span style="color:green Run a loop to check input for each item.
<span style="color:blue For <span style="color:blue Each tile <span style="color:blue In _GameTiles
<span style="color:blue If MouseButtons = Windows.Forms.MouseButtons.Left <span style="color:blue Then
<span style="color:blue If tile.Bounds.Contains(PointToClient(MousePosition)) <span style="color:blue Then
tile.OnInput(_GameTime)
<span style="color:blue End <span style="color:blue If
<span style="color:blue End <span style="color:blue If
<span style="color:blue Next
<span style="color:green Run a loop to draw each item after determining which
<span style="color:green buffer to draw on this frame
<span style="color:blue Dim gfx <span style="color:blue As Graphics
<span style="color:blue If _BufferFlag <span style="color:blue Then
gfx = Graphics.FromImage(_Buffer1)
<span style="color:blue Else
gfx = Graphics.FromImage(_Buffer2)
<span style="color:blue End <span style="color:blue If
gfx.Clear(BackColor)
<span style="color:blue For <span style="color:blue Each tile <span style="color:blue In _GameTiles
tile.OnDraw(_GameTime, gfx)
<span style="color:blue Next
<span style="color:green Cleanup and swap buffers
gfx.Dispose()
_BufferFlag = <span style="color:blue Not _BufferFlag
<span style="color:green Show the drawn scene
Invalidate()
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue Protected <span style="color:blue Overrides <span style="color:blue Sub OnPaint(e <span style="color:blue As System.Windows.Forms.PaintEventArgs)
<span style="color:blue MyBase.OnPaint(e)
<span style="color:green Draw the approprite render buffer
<span style="color:blue If _BufferFlag <span style="color:blue Then
e.Graphics.DrawImageUnscaled(_Buffer2, Point.Empty)
<span style="color:blue Else
e.Graphics.DrawImageUnscaled(_Buffer1, Point.Empty)
<span style="color:blue End <span style="color:blue If
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue End <span style="color:blue Class
<span style="color:blue Public <span style="color:blue Class GameTile
<span style="color:blue Public <span style="color:blue Property Location <span style="color:blue As Point
<span style="color:blue Public <span style="color:blue Property FrameImages <span style="color:blue As <span style="color:blue New List(Of Image)
<span style="color:green this is the images per second of the animation
<span style="color:blue Public <span style="color:blue Property FrameRate <span style="color:blue As <span style="color:blue Double = 8.0
<span style="color:green this is the total time to animate after recieving a click
<span style="color:blue Private _AnimationTime <span style="color:blue As <span style="color:blue Double
<span style="color:blue Public <span style="color:blue ReadOnly <span style="color:blue Property Bounds <span style="color:blue As Rectangle
<span style="color:blue Get
<span style="color:blue Return <span style="color:blue New Rectangle(Location, FrameImages(CurrentFrameIndex).Size)
<span style="color:blue End <span style="color:blue Get
<span style="color:blue End <span style="color:blue Property
<span style="color:blue Private _FrameIndex <span style="color:blue As <span style="color:blue Double
<span style="color:blue Public <span style="color:blue ReadOnly <span style="color:blue Property CurrentFrameIndex <span style="color:blue As <span style="color:blue Integer
<span style="color:blue Get
<span style="color:blue Return <span style="color:blue CInt(Math.Floor(_FrameIndex))
<span style="color:blue End <span style="color:blue Get
<span style="color:blue End <span style="color:blue Property
<span style="color:blue Public <span style="color:blue Sub OnInput(gameTime <span style="color:blue As GameTime)
<span style="color:green set the remaining animation time to 3 seconds when clicked
_AnimationTime = 3.0
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue Public <span style="color:blue Sub OnDraw(gameTime <span style="color:blue As GameTime, gfx <span style="color:blue As Graphics)
<span style="color:green draw the current frame at its current location
gfx.DrawImageUnscaled(FrameImages(CurrentFrameIndex), Location)
<span style="color:green if there is remaining animation time, then animate
<span style="color:blue If _AnimationTime > 0 <span style="color:blue Then
_FrameIndex += gameTime.LastFrame * FrameRate
<span style="color:blue If CurrentFrameIndex = FrameImages.<span style="color:blue Count <span style="color:blue Then _FrameIndex = 0.0
_AnimationTime -= gameTime.LastFrame
<span style="color:blue Else
_FrameIndex = 0.0
<span style="color:blue End <span style="color:blue If
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue End <span style="color:blue Class
<span style="color:green GameTime can be a simple structure or class which just tracks executed
<span style="color:green game time based on what the game loop tells it
<span style="color:blue Public <span style="color:blue Structure GameTime
<span style="color:blue Public ElapsedTime <span style="color:blue As TimeSpan
<span style="color:blue Public LastFrame <span style="color:blue As <span style="color:blue Double
<span style="color:blue Public <span style="color:blue Sub Elapse(milliseconds <span style="color:blue As <span style="color:blue Long)
ElapsedTime += TimeSpan.FromMilliseconds(milliseconds)
LastFrame = milliseconds / 1000
<span style="color:blue End <span style="color:blue Sub
<span style="color:blue End <span style="color:blue Structure
[/code]
<br/>
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
http://create.msdn.com/en-US/" target="_blank 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!).
http://unity3d.com/ 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
http://gdigaming.codeplex.com" target="_blank 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 are 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 main error I have is a poor design of the AssetManager and I just have to recode it â not a big deal
but it changes a lot of code so Iâm waiting until I know I have time to fix it right. The other minor issues are already corrected and pending this big one for a new publish.
Anyway, the project is still 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! =)
<
Reed Kimble - "When you do things right, people wont be sure youve done anything at all"<br/>
View the full article