Cesare
Cesare

Reputation: 69

Making Games in VB and optimisation

This is more of a general question than one linked to a particular project. Since I've started learning programing at school we have been using the language VB. I wanted to develop my skills and do some cool projects. Recently I've been making some small games and even entered Ludum Dare 32.

However, one big problem I keep running into is that as my games become more complicated and have more elements to them (especially increasing the amount of picture boxes), they become painfully slow. In my entry to Ludum Dare nearly all the comments talked about how the game starts off fine, but gets slower and slower as it goes on.

Here are some screenshots of a pacman style game I'm currently making:

Game with 2 AI (Orange) and 1 player (Green)

enter image description here

Same game with 3 AI (Orange) and 1 player (Green), about 15 more lines of code

enter image description here

As you can see in the second screenshot adding one more AI (15 lines of code and a picture box) halves the FPS of the game. I was hoping someone could help explain better ways to optimise games in VB.

Details about the game:

If anyone needs further information, please just ask.

Upvotes: 1

Views: 656

Answers (2)

user4842163
user4842163

Reputation:

This question sparked my interest even though I have very little knowledge about VB. I come originally from a gaming background (but back during the early 90s in the days of mode 13h VGA graphics, Borland Turbo C, assembly, AdLib synthesizers, etc), and I want to address the more general side of your question with a more general answer.

From what I can tell, you're using these kinds of visual form elements to try to model game sprites and tiles. I'm guessing you're doing animations consisting of changing x/y position-type properties over time and swapping out images and things like that, creating new controls on the fly, etc etc.

My suggestion, and it might be at the risk of being somewhat anti-VBish, is don't. For games these kinds of high-level controls and form elements can actually make your life harder. In some ways, these high-level, event-driven systems are more difficult to work with than back during the days when we were just manipulating video memory directly with a fullscreen application running a loop. These kinds of games benefit more from synchronous, centralized processing than asynchronous, decentralized processing of the kind where your code is scattered across events outside of your control.

With these kinds of retro-style arcade games, you want a structure that has a "main loop", something like this (simplified):

while the game is running:
    process input and game logic
    draw frame (to offscreen buffer if you can afford it)
    swap/copy your hidden buffer to front, visible one

It's going to make your life a lot easier if you can find a way to get this kind of structure in VB, and to do your own drawing manually with a drawing library (bonus points if it can do things like fast alpha blitting of sprites).

For example, maybe the main loop is not proper for a heavily event-driven system, and I suspect VB fits this category. But, for example, you might want to consolidate all those high-level timers into a single timer to emulate the game loop. Multiple timers will tend to make it hard to keep everything in sync.

With games you want to kind of be a control freak when it comes to the timing and drawing of things, and it'll give you a lot more room to optimize (ex: using acceleration structures like grids or quad-trees to avoid many superfluous drawing calls of offscreen objects with the minimum amount of checks, in addition to fast collision detection).

You don't need anything more from a rendering side than a blank screen/window and a good, fast set of drawing routines.

For example, you don't want a case where you have two enemies in the game, and one moves a single pixel in one frame and the other moves a single pixel in the next frame if they're both supposed to be moving simultaneously. That gives a kind of stiff and odd feeling which doesn't feel quite right, and it might be difficult to avoid if you aren't in control of exactly how the drawing/refreshes are being done on a frame-by-frame basis.

Become that control freak, mastering your own drawing and input processing with frame-by-frame control, and I'd actually suggest that it'll make your life a lot easier and help expand your horizons to making much more dynamic games.

Lastly, I'd suggest checking out how people wrote games for consoles like Nintendo and Atari and how people have continued that trend today. There should be some excellent resources on the subject out there, and I'd suggest starting with the idea of the main game loop and how drawing and animation and collision is typically done. They don't do it with a bunch of picture boxes and timers, e.g., and not because they didn't have that luxury back then, but because such things would have become obstacles.

On the plus side, you don't need to be writing assembly code these days to make cool 2D games with beautifully smooth sprite animations and a good frame rate. You could write them with high-level interpreters and you would be fine as long as your drawing library and possibly sound library/mixer is fast. What you just want to make sure is that you are in control of exactly what to draw and when. You want that kind of main loop mentality.

Upvotes: 2

Hans Passant
Hans Passant

Reputation: 942368

PictureBox is a convenience control. Convenience and speed are opposing goals, there is nothing that the control does to force you to do the right thing. The things you have to pay attention to, in order of their affect on rendering speed:

  • It automatically rescales an image to fit the control and the SizeMode you asked for. It uses a high quality rescaling algorithm that produces the best possible image, it burns a lot of cpu cycles. And in order to not use too much memory, it does so every single time it paints itself. You must avoid this, only use SizeMode = Normal avoid this cost. Which in turn usually means that you have to pre-scale the image yourself before you assign the Image property.

  • The pixel format of the Image is very, very important. There is only one that's fast and permits the bitmap to copied straight to the video adapter's frame buffer without having to be converted. That is PixelFormat.Format32bppPArgb on any modern machine. That is almost never the pixel format of a bitmap that you create with a painting program. A conversion is required before you assign the Image property. It matters a lot, a 32bppPArgb image renders ten times faster than all the other ones.

  • It supports transparency by setting the BackColor to Color.Transparent. Implemented by first letting the Parent draw itself into the control window, then painting the Image on top of it. This must be avoided, it more than doubles the rendering time. Set the PictureBox.Size to match the image size. If you need the transparency in the image then don't use PictureBox at all but instead call e.Graphics.DrawImage() in the parent's Paint event handler.

  • PictureBox is not convenient for resource management, it does not take ownership of the bitmap you assign to the Image property. It is very common to forget to call the image's Dispose() method. In some cases that can cause your app to consume a lot of memory and causing slowdowns. You typically notice by your FPS dropping after playing the game for a while. You must write the Dispose() calls in your FormClosed event handler or whenever you re-assign the Image property.

And there are additional speed-ups that you can't get out of PictureBox. On top of that list is storing the image in the video adapter's memory instead of the machine's main RAM. The video RAM is clocked far faster than the main RAM so the bitmap copy is much faster. That requires DirectX, a very common choice in game programming. The SharpDX and SlimDX libraries are popular.

Some code that helps you deal with these bullets:

Public Shared Sub GenerateFastImage(pbox As PictureBox, img As Image, ownsImage As Boolean)
    '' Calculate effective size, like SizeMode = AutoSize
    Dim zoom = Math.Min(CDbl(pbox.ClientSize.Width) / img.Width, _
                         CDbl(pbox.ClientSize.Height) / img.Height)
    Dim size = New Size(CInt(zoom * img.Width), CInt(zoom * img.Height))
    Dim pos = New Point((pbox.ClientSize.Width - size.Width) \ 2, _
                        (pbox.ClientSize.Height - size.Height) \ 2)
    '' Generate 32bppPArgb image
    Dim bmp = New Bitmap(pbox.ClientSize.Width, pbox.ClientSize.Height, _
                         System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
    Using gr = Graphics.FromImage(bmp)
        gr.Clear(pbox.BackColor)
        gr.DrawImage(img, New Rectangle(pos, size),
                     New Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel)
    End Using
    '' Dispose images
    If ownsImage Then
        If pbox.Image IsNot Nothing Then pbox.Image.Dispose()
        img.Dispose()
    End If
    pbox.Image = bmp
    pbox.SizeMode = PictureBoxSizeMode.Normal
End Sub

Pass True for the ownsImage argument if the image displayed in the picture box is not used anywhere else in your program. Sample usage for this method:

Public Sub New()
    InitializeComponent()
    GenerateFastImage(PictureBox1, My.Resources.Player, True)
    GenerateFastImage(PictureBox2, My.Resources.Monster, True)
End Sub

Upvotes: 5

Related Questions