vbasicnet
 
 
Reed Kimble Micro Plastics, Inc.

http://social.technet.microsoft.com/wiki/contents/articles/17358.how-to-create-video-games-in-vb-net-windows-forms.aspx

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! =)
en-US, has code, has comment, has comments, VB.NET 2010, Video Games, visual basic, Windows Forms [Edit tags]

Deja un comentario