Relatively new VB hobbyist programmer here. I've been playing around with an application to make a "Space Invaders" type game --- I actually cut and pasted the code from a post I found on these forums. But the graphics are updating sloooowwwww... The spaceship goes left and right but the background doesn't redraw until you stop moving because the system is chugging along. I was trying to Google some sites for optimizing VB graphics or just plain VB code in general. I found some stuff on VB code --- not a lot but a little. I could find hardly anything on optimizing graphics though. Does anyone have any good tips for how to get graphics to draw faster? I think I'm using either GDI or GDI+ (like I said --- new programmer =)).
I know I'm trying to run before I learn to walk on VB by jumping into a game so soon but I'm having fun doing this. Really I just looking for A) some tips on optimizing or B) good links I can educate myself further. Any help is appreciated!!!
There used to be templates available on the Microsoft pages one of which was an introduction to games programming and it was called SpaceBlitz Starter Kit.
I could not find the template on line but found a copy on my HD from a couple of years ago. I don't know how it functions but there is a "Documentation" folder that talks about sprites, keyboard control etc.
It may be outdated now and thats why I could not locate it online , I don't know, but it may contain something useful for you.
Thank you for the replies --- I will try the double buffering MotoX646 --- thanks for the tip. And I'll check out SpaceBlitz also --- thanks Mc_VB.
For Paul's query --- I will post the code a bit later. I am on a work computer now and the file I'm working with is on my laptop back in the room. Basically though, I copied a post in here the said "Need help with my Space Invaders game". I used that code as a starting point. Where I went to with it is pretty much unrecognizable from where I started though (other than maybe the section that takes keyboard input). Last night I got everything working properly ---- ship fires laser, alien gets hit, points update, if alien hits ship - ship disappears and game over pops up. So that was cool --- but the graphics performance still sucks.
Anyways, I'm getting long winded. I'll try the tip and post my code later (probably tomorrow). Thanks for the help guys!
When you are drawing moving or changing images you need to do it on a double buffered control surface. If you are drawing on a Form (and you are using VB2005+) just set the form's DoubleBuffered property to True (in the Designer or in Code). The SetStyles statements are no longer needed. But double buffering the form doesn't double buffer the form's child controls. If you are drawing on a PictureBox, there is no need to do anything because it's double buffered anyway. Panels can also be made double buffered by inheriting. Ignore the Bob Powell link: apparently he didn't realize the Framework already provided double buffering when he wrote that (2003).
Still, your description sounds more like a problem with Invalidating/Refreshing. For efficiency invalidate only the dirty rectangles (previous and new positions). Don't use Refresh or Update when dragging because it will just slow things down.
Ok, I got the code last night and copied it to my flash drive. It is short and sweet. The code I copied from these forums was much longer but I deleted much of it that I thought was unnecessary and I tinkered with other things until I got the program working right.
So, here's where I'm at: Double buffering has greatly improved performance, thanks MOTOX646 and boops boops! I still have some graphics issues that are weird --- when the alien comes down the screen, roughly the top 10% of his head is "doubled" --- looks weird. It is the same with your ship when you move it left or right. If you move left then the right 10% of the ship is doubled while you're moving. If you move right then the left 10% of your ship is doubled.
Ok, one other thing that I just thought of last night that is probably at the heart of all this stuff. When I first installed VB Express 2010 on my laptop, I kept crashing on startup and I would get an error message that "video driver crashed and has recovered". I went online and found that this was a conflict between my Radeon video drivers and Microsoft WPF. So I disabled WPF and the error stopped ---- but I don't remember how I disabled it!!! There's nothing in the VB options... and online it says to check the registry --- the tree leads to a directory called Avalon.graphics --- which I checked my registry and that key isn't even in there. Hrmmmmm, not sure where to go now. Ok, but if you guys want to tell me if you see anything with the code while you're at it, here it is:
Public Class Board
'All images created in Microsoft Paint and then imported to PictureBox controls
'Objects in this program: Board(Space-themed background), Ship(player's ship), Laser(small "beam" graphic), Alien(descends towards player),
'GameOver(label in center of screen), Score(Label in upperright of background)
'Possible reason for problems... think I remember having to switch off WPF on VB Express because it was conflicting with Radeon driver? Can't
'find area to switch it back on and check though --- probably wounldn't matter if I can't use with Radeon anyways - at least on my laptop
'Problems --- Graphics updating sucks!!! Double buffering has helped but user holding down input key kills all action until user lets up
'Scoring is not updating properly: When laser hits alien, it is supposed to assign score of 1,000, then get rid of the alien. Instead it
'counts up to 5 times on the same issue and awards a score of 5,000. Not a huge deal - just don't understand why not working properly
Dim plScore As Integer = 0
Private Sub Board_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
tmrMain.Start() 'This is the main clock for the game
Laser.Visible = False
GameOver.Visible = False
Score.Text = "0000000" 'Display Expression of players Score
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
Me.SetStyle(ControlStyles.UserPaint, True)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.UpdateStyles()
End Sub
Private Sub Board_KeyPress(ByVal eventsender As Object, ByVal eventArgs As System.Windows.Forms.KeyPressEventArgs) Handles MyBase.KeyPress
Dim Input As Short = Asc(eventArgs.KeyChar)
'In game controls
Select Case Input
Case Asc("a")
Me.Ship.Left = Me.Ship.Left - 10
Case Asc("d")
Me.Ship.Left = Me.Ship.Left + 10
Case Asc("x")
End
Case Asc(" ")
Laser.Visible = True
Laser.Location = New System.Drawing.Point((Ship.Location.X + 40), Ship.Location.Y - 20)
End Select
End Sub
'This section moves the enemies down, moves you laser Laser up the screen and destroys the alien if you shoot it
Private Sub tmrMain_tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrMain.Tick
Me.Alien.Top = Me.Alien.Top + 5
If Laser.Visible = True Then Laser.Top -= 10
If Laser.Bounds.IntersectsWith(Alien.Bounds) Then Alien.Visible = False : plScore += 1000 : Score.Text = CStr(plScore)
If Alien.Bounds.IntersectsWith(Ship.Bounds) And Alien.Visible = True Then Ship.Enabled = False : Ship.Visible = False : GameOver.Visible = True
End Sub
user holding down input key kills all action until user lets up
I suspect this is because you're using the Form_KeyPress event which is notoriously unreliable as a receiver. I suggest that you add all the controls on the form eg.
vb.net Code:
Private Sub Board_KeyPress(ByVal eventsender As Object, ByVal eventArgs As System.Windows.Forms.KeyPressEventArgs) Handles MyBase.KeyPress, Control1.KeyPress, Control.Keypress
This will make sure that any keypress is handled irrespective of where the focus is. (Incidentally, is there any particular reason for departing from the default sender and e variables in the sub definition?)
'Scoring is not updating properly: When laser hits alien, it is supposed to assign score of 1,000, then get rid of the alien. Instead it
'counts up to 5 times on the same issue and awards a score of 5,000. Not a huge deal - just don't understand why not working properly
This one's easy! You don't get rid of the alien you merely make it invisible. Just because you can't see it doesn't mean it's not there and as you're using its bounds rather than any visible property for collision detection it is very much still there.
To answer your question on whether there was any reason to depart from the default sender and e variables --- I'm afraid I don't know enough about Visual Basic to answer that question =( I cut-and-pasted the code from someone else that posted on the forums and then chopped out the unnecessary bits and switch some code positions around. The event coding is a left over from my cut-and-paste job ---- I really don't know enough about event coding ---- as a matter a fact, I know next to nothing about it other than it pops up when I double-click an object on a form =)
So your 1st suggestion about adding all the form objects is a bit lost on me --- I might be able to play around with it and figure out what you mean but I can already visualize myself getting frustrated. Still, I'm going to try your suggestion(s) and see if I can figure them out ---- I'm usually clever enough to get myself in trouble at least...
I have been learning from several different books and probably jumping around too much. But I am having much more fun trying to make a game than I am writing a business application to figure out compound interest... that's for sure!
So thanks again Dunfiddlin and thanks again to everyone who has responded ---- these forums are great! It's nice to be able to get some advice on what I'm trying to do.
Ok, let me try this again --- I had posted everything including the new code but then my internet crashed --- <grumble>
I'm not going to post the code again (mainly because I didn't copy it on to my flash drive and I'm on my work computer). But here's what I've tried so far. Per Dunfiddlin's suggestion, I started focusing on how I was taking user input. I added all the objects in the program to the event handler --- no change. Then I surfed to MSDN to see what they had to say on single key input. They still used the keypress method but their sender (and whatever the 2nd variable after the comma is called) was closer to what Dunfiddlin wondered why I departed from. So I used that... and I axed the 'select case' in favor of if statements using the ASCII code for each character. I figured I was really onto something --- then I compiled and the exact same behavior.... lol, doh!!!!
Ok... so.... here's what I'm thinking. Tell me if it sounds like I'm on to something. When you move the ship left or right and hold the key down --- the alien stops moving. Haven't checked the laser (should have done that.... will when I head back to the room for the night). But so what I'm wondering is ---- while VB is running the keypress method ---- or any other method I guess ---- can this cause the timertick event to stop or pause? I would think they'd be asynchronous but something's going on....
The only other thing was I ran across a program called EZInvaders online and they said some users of their programs tried to put an image in the background form and it causes what sounded like the same behavior as I'm having in my program.
Any thoughts on this??
I'll keep trying to reserach solutions as well but you guys are just so damn convenient!!! haha, seriously --- I would have never figured out the double buffering thing... and more ideas are always welcome. Tinkering is more fun when you have an idea what to tinker with.... =)
while VB is running the keypress method ---- or any other method I guess ---- can this cause the timertick event to stop or pause?
It most certainly can. If you have loops or recurring events such as keypress when the key is held down it makes sense to add....
Application.DoEvents()
... at strategic points. This ensures that events happen when they're supposed to happen and, perhaps more crucially, that the form is updated.
As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"
Reviews: "dunfiddlin likes his DataTables" - jmcilhinney
Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!
It most certainly can. If you have loops or recurring events such as keypress when the key is held down it makes sense to add....
Application.DoEvents()
... at strategic points. This ensures that events happen when they're supposed to happen and, perhaps more crucially, that the form is updated.
No, don't do this, it is not necessary. If you need to do something that requires this then you will be hiding a flaw in the program - redesign your program.
"Ok, my response to that is pending a Google search" - Bucky Katt. "There are two types of people in the world: Those who can extrapolate from incomplete data sets." - Unk. "Before you can 'think outside the box' you need to understand where the box is."
Here's my take of a 'space invaders' in a quick 15 minute hack-up of code:
Code:
Public Class Form1
Private WithEvents DisplayTimer As Windows.Forms.Timer
Private Rand As New Random
Private monster As Alien
Private ship As Citizen
Private laser As Gun
Private score As Integer
Private Class Alien
Public Property Visible As Boolean
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
Public Property Speed As Integer
End Class
Private Class Citizen
Public Sub New()
Width = 20
Height = 10
End Sub
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
End Class
Private Class Gun
Public Sub New()
Width = 3
Height = 8
End Sub
Public Property visible As Boolean
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
End Class
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
Me.SetStyle(ControlStyles.UserPaint, True)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.UpdateStyles()
End Sub
Private Sub Board_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
DisplayTimer = New Windows.Forms.Timer
monster = New Alien
ship = New Citizen
laser = New Gun
ship.X = Me.ClientSize.Width \ 2
ship.Y = Me.ClientSize.Height - 20
DisplayTimer.Interval = 30
DisplayTimer.Enabled = True
End Sub
Protected Overrides Sub OnKeyDown(e As System.Windows.Forms.KeyEventArgs)
MyBase.OnKeyDown(e)
If e.KeyCode = Keys.A Then
ship.X -= 10
ElseIf e.KeyCode = Keys.D Then
ship.X += 10
ElseIf e.KeyCode = Keys.Space Then
If laser.visible = False Then
laser.X = ship.X + ship.Width \ 2 - laser.Width \ 2
laser.Y = ship.Y - laser.Height
laser.visible = True
End If
End If
End Sub
Protected Overrides Sub OnResize(e As System.EventArgs)
MyBase.OnResize(e)
Me.ship.Y = Me.ClientSize.Height - 20
End Sub
Private Sub DisplayTimer_Tick(sender As Object, e As System.EventArgs) Handles DisplayTimer.Tick
If monster.Visible Then
monster.X += monster.Speed
If monster.X > Me.ClientSize.Width Then monster.Visible = False
Else
monster.Visible = True
monster.Width = Rand.Next(20, 40)
monster.Y = Rand.Next(0, 100)
monster.Height = Rand.Next(10, 20)
monster.Speed = Rand.Next(1, 10)
monster.X = -monster.Width
End If
'
If laser.visible Then
Dim oldY As Integer = laser.Y
laser.Y -= 10
If laser.Y + laser.Height < 0 Then laser.visible = False
' Check for collision
Dim r As New Rectangle(laser.X, laser.Y, laser.Width, laser.Y - oldY + laser.Height)
If r.IntersectsWith(New Rectangle(monster.X, monster.Y, monster.Width, monster.Height)) Then
' HIT
monster.Visible = False
laser.visible = False
score += 1
End If
End If
' perform a redraw
Me.Invalidate()
End Sub
Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(e)
' draw the monster
If monster.Visible Then e.Graphics.FillRectangle(Brushes.Red, monster.X, monster.Y, monster.Width, monster.height)
' draw the ship
e.Graphics.FillEllipse(Brushes.DarkBlue, ship.X, ship.Y, ship.Width, ship.Height)
' draw the laser
If laser.visible Then e.Graphics.FillRectangle(Brushes.Green, laser.X, laser.Y, laser.Width, laser.Height)
' draw the score
e.Graphics.DrawString(score.ToString, Me.Font, Brushes.Black, 0, 0)
End Sub
End Class
"Ok, my response to that is pending a Google search" - Bucky Katt. "There are two types of people in the world: Those who can extrapolate from incomplete data sets." - Unk. "Before you can 'think outside the box' you need to understand where the box is."
Yes, well, sometimes maybe it's not such a bad idea to cover up the 'flaws' in your program! Your code, ironically, starts with an unhandled exception (well several actually) and, more pertinently, when edited to make it work, can't fire and move at the same time (oh, and on one occasion I swear the 'bullet' passed straight through the middle of an 'alien' and had no effect on it whatsoever but that could just be my eyes). Right now, I'm not recommending it as a learning experience if that's ok.
As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"
Reviews: "dunfiddlin likes his DataTables" - jmcilhinney
Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!
Yes, well, sometimes maybe it's not such a bad idea to cover up the 'flaws' in your program! Your code, ironically, starts with an unhandled exception (well several actually) and, more pertinently, when edited to make it work, can't fire and move at the same time (oh, and on one occasion I swear the 'bullet' passed straight through the middle of an 'alien' and had no effect on it whatsoever but that could just be my eyes). Right now, I'm not recommending it as a learning experience if that's ok.
Regardless, telling people to do the wrong thing to hide their mistakes, I'll take your 'recommendation' with the due consideration it deserves.
The 'example' isn't about how to create a working invaders game, but how to correctly program graphics. The keyboard event(s) are functioning as designed: no amount of do-events or any shenanigans will correct that problem. Since graphics are the basic issue, the keypress issue can be corrected at a later point (it requires an Win32 API call, if I recall, but there may be a .NET equivalent).
To reiterate, the above code is for demonstration, not production code. But if you feel you can do better, go ahead and do so. I'm open to learning better ways of doing things in .NET.
"Ok, my response to that is pending a Google search" - Bucky Katt. "There are two types of people in the world: Those who can extrapolate from incomplete data sets." - Unk. "Before you can 'think outside the box' you need to understand where the box is."