Azerty560
Azerty560

Reputation: 125

How to Speed Up A Game made in VB.NET

As the title suggests, I am asking a question about a game I've made which I want to speed up. Its a pretty general question. So here it is: I have made a program where you are a circle and the enemies are squares coming at you. You press Space to shoot and you gain points every time you hit, using arrow keys to move, there are pickups, etc, etc.

However, I no longer want to use placeholder circles which are drawn like this:

e.Graphics.FillEllipse(Brushes.Orange,Player)

Player here is the rectangle that defines the player. So, to try and replace the graphics, I downloaded a JPEG image which is earth(player) vs Spaceships(Enemies). I drew it using:

e.Graphics.DrawImage(ImgEarth,Player.X,Player.Y,Player.Width,Player.Height)

and the same technique for drawing the enemies.

However, the result is very very slow moving aliens. Its not a slow timer or anything(I have about 2 in there working simultaneously) but poor FPS. It might be my system but when I checked it only took ~10MB of memory. I understand that VB.NET isn't quite the choice for game making since its not as fast as some of the other languages(C++, C#) but is there any way I can draw these without sacrificing FPS?

If it helps, I have a 1.66 GHz processor with GeForce 8400M GS. (Quite capable for something as simple as this in my opinion)

EDIT: I use double buffering. Me.DoubleBuffered = True. ImgEarth is a 310x310 image. I will try scaling it down, since on the form it is drawn as a 50x50 - good point.

Okay, here is the entire Form1.Paint Procedure. I'll explain everything after it.

e.Graphics.DrawImage(ImageEarth, Player.x,Player.y,Player.Width,Player.Height)

        'Projectiles
        Dim i As Integer = 0
        Do
            If Current_Projectile(i).IsEmpty = False Then e.Graphics.FillRectangle(Brushes.Red, Current_Projectile(i))
            i += 1
        Loop Until i = UBound(Current_Projectile)

        i = 0

        Do
            If Advanced_Projectiles(i).IsEmpty = False Then e.Graphics.FillRectangle(Brushes.Green, Advanced_Projectiles(i))
            i += 1
        Loop Until i = UBound(Advanced_Projectiles)


        i = 0
        Do
            If Pickups(i).IsEmpty = False Then
                If PickUpType = 0 Then e.Graphics.FillEllipse(Brushes.Green, Pickups(i))
                If PickUpType = 1 Then e.Graphics.FillEllipse(Brushes.Red, Pickups(i))
                If PickUpType = 2 Then e.Graphics.FillEllipse(Brushes.Yellow, Pickups(i))
            End If
            i += 1
        Loop Until i = UBound(Pickups)
        'Objects
        i = 0


        Do
            If Objects(i).IsEmpty = False Then e.Graphics.FillRectangle(Brushes.Blue, Objects(i))
            i += 1
        Loop Until i = UBound(Objects)

Okay. Current_Projectile is an array storing data about the projectiles being fired by the player(the bullets). Then, Advanced projectile is the same, except it fires a more 'potent' version of the former. Pickups is an array storing data about pickups that are being fired toward the player. The color of it is determined in another Subroutine. Lastly, objects(i) is an array that stores data about the enemy rectangles being fired at you.

EDIT 2: Okay, figured you guys might need the Timer.Tick procedure.

The timer is called Tmr_Main, and it handles about 90% of everything happening during the game. It delegates most of its work to individual subroutines, but some is done in the timer itself. This is the second most sophisticated block of code in the entire form. Here it is:

Dim i As Integer = 0

CollisionDetect()
CheckForDead()

If ReadyToReset = True Then ResetInt += 1
If ResetInt = 80 Then
    ResetPowerUps()
    ReadyToReset = False
    ResetInt = 0
End If
'Projectiles(Bullets)
i = 0
Do
    If Current_Projectile(i).IsEmpty = False Then
        Current_Projectile(i).X += Projectile_Speed

        If Current_Projectile(i).X > Me.Width Then

            Current_Projectile(i) = Nothing
            No_Of_Projectiles -= 1
        End If

    End If

    i += 1

Loop Until i = UBound(Current_Projectile)
'Advanced Projectiles
i = 0

Do
    If Advanced_Projectiles(i).IsEmpty = False Then
        Advanced_Projectiles(i).X += AdvProjectile_Speed
    End If
    If Advanced_Projectiles(i).X >= Me.Width Then
        Advanced_Projectiles(i) = Nothing
    End If
    i += 1
Loop Until i = UBound(Advanced_Projectiles)

'Pickups
GeneratePickups()

i = 0
Do
    If Pickups(i).IsEmpty = False Then
        Pickups(i).X -= Pickup_Speed
    End If
    If Pickups(i).X < 0 Then
        Pickups(i) = Nothing
    End If
    i += 1
Loop Until i = UBound(Pickups)


LblScore.Text = "Score: " & Score
CheckForWin()



Me.Invalidate()

Okay, once again, explaining everything from the top. CollisionDetect() contains logic for evaluating what has hit what in the entire game. This includes Player and Pickup, Player and Object, Object and Projectile, Object and Advanced Projectile, Object and Player. Next, it evaluates whether or not if a pickup has been used or not. When the player hits a pickup, a boolean is set to true. This means that every iteration of this timer, one is added to variable ResetInt. When this hits 80, or 4 seconds(50ms timer) the pickup's effects are reset. Additionally, the boolean is false, so it won't do that same again.

Next, it moves all of the objects on the form. These include the the objects, projectiles and pickups. It also checks if they have reached the end of their 'path' (ie If the projectile goes far enough to the right, it is removed from the game).

Lastly, it checks whether the player has won or not, and updates a label which tells the player about score. Then it redraws.

Ideas?

Upvotes: 2

Views: 2153

Answers (2)

rskar
rskar

Reputation: 4657

Some ideas to try...

(1) Use Option Strict On which at minimum will guide you away from any inefficient automatic type conversions.

(2) Instead of Do ... Loop Until i = UBound( ... ) use For i = 0 To UBound( ... ) - 1 (unless you're really expecting the array to possibly add or lose elements during the loop); For will call UBound only once while Do will call it with every iteration. Better yet, why not use Length member of array instead of UBound?

(3) For those If statements testing PickUpType, go with an If-ElseIf ladder:

If PickUpType = 0 Then 
    e.Graphics.FillEllipse(Brushes.Green, Pickups(i)) 
ElseIf PickUpType = 1 Then 
    e.Graphics.FillEllipse(Brushes.Red, Pickups(i)) 
ElseIf PickUpType = 2 Then 
    e.Graphics.FillEllipse(Brushes.Yellow, Pickups(i)) 
End If

After all, if PickUpType = 0, why waste time with PickUpType = 1 and PickUpType = 2?

(4) Experiment with With ( http://msdn.microsoft.com/en-us/library/wc500chb(v=vs.100).aspx ). Instead of:

If Advanced_Projectiles(i).IsEmpty = False Then 
    Advanced_Projectiles(i).X += AdvProjectile_Speed 
End If 

Try:

With Advanced_Projectiles(i)
    If Not .IsEmpty Then 
        .X += AdvProjectile_Speed 
    End If 
End With

(5) If you're using anything (not a constant) out of the Microsoft.VisualBasic namespace (such as UBound), try to find a way to accomplish the same in the more "standard" parts of the Framework. Microsoft.VisualBasic may have additional layers or VB6 quirks which you'll likely not need.

Upvotes: 4

tcarvin
tcarvin

Reputation: 10855

You should be using double-buffering. This is where you perform all of your painting and drawing to an in-memory bitmap in response to game events etc. Then in your OnPaint you perform a single blit to the screen of that back-buffer.

Upvotes: 1

Related Questions