Too many bullets
Too many bullets
Too many bullets
Too many bullets
Too many bullets
Too many bullets Too many bullets Too many bullets Too many bullets Too many bullets Too many bullets Too many bullets Too many bullets
Too many bullets Too many bullets
Too many bullets
Go Back  Xtreme Visual Basic Talk > > > Too many bullets


Reply
 
Thread Tools Display Modes
  #1  
Old 04-22-2011, 09:14 AM
1246 1246 is offline
Newcomer
 
Join Date: Jul 2010
Posts: 6
Default Too many bullets


Hello.

I am making a 2D game that has a player (me) that shoots bullets (pictureboxes) and kills other enemies. The problem is that when there are many bullets on the screen it gets very slow because it has to move every picturebox.
Is there any work around this? Thanks.
Reply With Quote
  #2  
Old 04-22-2011, 10:21 AM
AtmaWeapon's Avatar
AtmaWeaponToo many bullets AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Controls (pictureboxes in particular) are a very heavyweight way to draw. You tend to get better performance for multiple moving elements by using the Paint event and drawing things yourself. If you dig around these forums and on the internet there are thousands of examples.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #3  
Old 04-22-2011, 10:42 AM
1246 1246 is offline
Newcomer
 
Join Date: Jul 2010
Posts: 6
Default

Thanks a lot, AtmaWeapon.
Now it doesn't drop many FPS, but it is still noticeable when a bullet shoots.
FPS are at 65 when there is no bullet movement but when 20 bullets are moving the FPS drop to ~40. Before it used to drop to ~20!

Here's how the paint code looks like:

Code:
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint

        If active_bullets > 0 Then
            For i As Integer = 0 To bullet.Length - 1
                If bullets.Contains("|" & i & "|") Then
                    e.Graphics.DrawRectangle(Pens.Orange, bullet(i).Bounds)
                End If
            Next
        End If

End Sub

But then there's the problem that I can't specify the bullet size, and so they look kind of big!
Reply With Quote
  #4  
Old 04-22-2011, 11:22 AM
AtmaWeapon's Avatar
AtmaWeaponToo many bullets AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Ideally, your painting code should do as little work as possible. Right now you're doing too much work.

Your code snippet leaves a lot of details out, so I'm going to put my guesses out here. I'm assuming:
  • active_bullets is an Integer that represents the number of active bullets.
  • bullets is a string whose purpose I cannot deduce.
  • bullet is an array of PictureBox that represents each bullet.
It's silly to have pictureboxes at all. Whether they're invisible or not, your form's painting code has to manipulate them. Changing the location of a picture box causes the form to repaint, which takes time. Don't use a control because it happens to have a property of a type you want: make the property yourself.

What if you had a list of rectangles called activeBullets that only contained the bounds of the active bullets? Then your code would look like this:
Code:
ForEach bullet As Rectangle In activeBullets
    e.Graphics.DrawRectangle(Pens.Orange, bullet)
Next
Now you're not paying the penalty of invisible controls, and you cut out the Contains() call which was probably dragging you down as well.

I'm confused why you think you can't specify the bullet size. The second argument to DrawRectangle() is the bounds of the rectangle that will be drawn. If you want smaller bullets, make your rectangles smaller.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #5  
Old 04-22-2011, 04:08 PM
1246 1246 is offline
Newcomer
 
Join Date: Jul 2010
Posts: 6
Default

Quote:
Originally Posted by AtmaWeapon View Post
Ideally, your painting code should do as little work as possible. Right now you're doing too much work.
Well, every bit of it is necessary:

Code:
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint

        If active_bullets > 0 Then ' if there are active bullets
            For a As Integer = 0 To bullet.Length - 1
                If bullets.Contains("|" & a & "|") Then ' if the string variable that holds every active bullet (the number) contains the current bullet number
                    e.Graphics.DrawRectangle(Pens.Orange, bullet(a))
                End If
            Next
        End If

End Sub
Quote:
Originally Posted by AtmaWeapon View Post
Your code snippet leaves a lot of details out, so I'm going to put my guesses out here. I'm assuming:
  • active_bullets is an Integer that represents the number of active bullets.
  • bullets is a string whose purpose I cannot deduce.
  • bullet is an array of PictureBox that represents each bullet.
active_bullets is indeed an integer that holds the active bullets number;
bullets is a string that holds every active bullet's number. To explain this properly it would take a lot time! Every bullet number is split by a "|";
bullet is also indeed an array of pictureboxes that represents each bullet.

Quote:
Originally Posted by AtmaWeapon View Post
It's silly to have pictureboxes at all. Whether they're invisible or not, your form's painting code has to manipulate them. Changing the location of a picture box causes the form to repaint, which takes time. Don't use a control because it happens to have a property of a type you want: make the property yourself.
Okay, so I changed it to use rectangles instead of pictureboxes. Now instead of taking ~20 FPS with 30 bullets, it takes ~5 FPS with 50! I see a big improvement!

Quote:
Originally Posted by AtmaWeapon View Post
What if you had a list of rectangles called activeBullets that only contained the bounds of the active bullets? Then your code would look like this:
Code:
ForEach bullet As Rectangle In activeBullets
    e.Graphics.DrawRectangle(Pens.Orange, bullet)
Next
Now you're not paying the penalty of invisible controls, and you cut out the Contains() call which was probably dragging you down as well.
I'm sorry, can you explain this better? I didn't quite understand it.

Quote:
Originally Posted by AtmaWeapon View Post
I'm confused why you think you can't specify the bullet size. The second argument to DrawRectangle() is the bounds of the rectangle that will be drawn. If you want smaller bullets, make your rectangles smaller.
A bullet's size is set to 10 width and 1 height but when it is drawn with the paint event, the height doubles to 2. With pictureboxes this didn't happen.

Thanks for the help.
Reply With Quote
  #6  
Old 04-22-2011, 05:12 PM
AtmaWeapon's Avatar
AtmaWeaponToo many bullets AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

I'd like to see more of your code; more on that later.

Quote:
Okay, so I changed it to use rectangles instead of pictureboxes. Now instead of taking ~20 FPS with 30 bullets, it takes ~5 FPS with 50! I see a big improvement!
Sarcasm is best applied when your data is correct. Absent any code I cannot comment on this. I can point out that you're not doing an apples to apples comparison. So it's slower with 50 bullets than it was with 30. That's logical: you're doing more work. What did it do with 30? If it wasn't within a few FPS of before, what's the code look like? Perhaps you had an error.

Quote:
I'm sorry, can you explain this better? I didn't quite understand it.
Yes, but I'm going to talk about it later.

Quote:
A bullet's size is set to 10 width and 1 height but when it is drawn with the paint event, the height doubles to 2. With pictureboxes this didn't happen.
I think I know what's going on, but I'm not certain without a peek at your code. Here's a guess. You can't display less than one pixel, so a rectangle with a stroke width of 1 and a desired height of 1 is, in effect, the same thing as a line. The DrawRectangle() call has a known little bit of overlap that's apparently by design, so a height 1 rectangle ends up as 2? Or maybe it's your monitor doing sub-pixel rendering. It could even be a screwup in your code. I dunno without being able to run and look at it myself.

It's at this point I'd really like to see more of your code. It helps me understand if something else is eating up your performance; maybe the UI thread is busy and the Paint event isn't being raised as often as you think. It helps me try solutions out to see if they work before sending you some bad advice. It also helps me solve a problem: I can't figure out how you're getting 60 FPS in a WinForms application. The common approach is to use a Timer, but the documentation points out it has a minimum resolution of about 55ms. 60 FPS would require a consistent timer resolution of 16.7ms; 55ms gets you about 18 FPS. I've done some experimentation with the more precise threaded timers and pulled off 60 FPS before, but it was quite complicated and took me a few days. So I'm curious how you're getting 60 FPS because it could be something I didn't know about and I'd like to see it. Alternatively, it could be that your framerate calculation's got a flaw and you aren't getting reasonable data. So at the very least I'd like to see how you're causing the Paint event to be raised at a rate of 60Hz.

Now, back to the code I explained. It sounds like you probably create bullets by some process like this:
Code:
bullets = bullets & "|" & bulletNumber & "|"
I suppose you remove a bullet through a more complicated process of digging through the string, finding the spot where the number is, and removing the characters that make up the string.

This process is a drain in and of itself. Strings in .NET are immutable, which means you can't change them without creating a new string. And since strings are arrays, the only way to make a new one is to create a new block of memory and copy values over. Generally, you don't want to manipulate a string within a loop if you can avoid it. If you can't avoid it, you want to use StringBuilder(), which is optimized for modification.

So what's another way to keep track of the active bullets? Well since what you wanted was a list of numbers an Integer list or array would make sense; then you wouldn't have to parse a string to figure out if there was a number. But there's a better way.

What if you had some Bullet class?
Code:
Public Class Bullet
    Public Property Number As Integer
    Public Property Bounds As Rectangle
End Class
Now what if you created a list for the active bullets?
Code:
Private _activeBullets As new List(Of Bullet)()
Now when you want to create a bullet, you add it to the list:
Code:
Dim newBullet As New Bullet()
newBullet.Number = bulletNumber
newBullet.Bounds = ...
_activeBullets.Add(newBullet)
When the bullet needs to be removed, you find it and remove it:
Code:
For i As Integer = 0 To _activeBullets.Length - 1
    If _activeBullets(i).Number = bulletNumber Then
        _activeBullets.RemoveAt(i)
        Return
    End If
Next
And, in a slight modification to the snippet from before, when you need to draw the active bullets you just loop over them and draw; some might even put a Draw() method on the Bullet class:
Code:
For Each bullet In _activeBullets
    e.Graphics.DrawRectangle(Pens.Orange, bullet.Bounds)
Next
Now you have no need for the string.

If you can, I'd like to see the code for the aforementioned reasons. I could produce an example, but I'm not confident I could reliably hit 60 FPS.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #7  
Old 04-22-2011, 06:20 PM
1246 1246 is offline
Newcomer
 
Join Date: Jul 2010
Posts: 6
Default

Quote:
Originally Posted by AtmaWeapon View Post
Sarcasm is best applied when your data is correct. Absent any code I cannot comment on this. I can point out that you're not doing an apples to apples comparison. So it's slower with 50 bullets than it was with 30. That's logical: you're doing more work. What did it do with 30? If it wasn't within a few FPS of before, what's the code look like? Perhaps you had an error.
I wasn't being sarcastic! The fact is that before it used to go down to ~15 FPS when many bullets were moving and now it is completely different! It mostly won't go down 60 FPS!
And I also don't know how it is possible that I get such a low frame rate, being that in a decent First Person Shooter I get ~100 FPS!

Quote:
Originally Posted by AtmaWeapon View Post
Now, back to the code I explained. It sounds like you probably create bullets by some process like this:
Code:
bullets = bullets & "|" & bulletNumber & "|"
I suppose you remove a bullet through a more complicated process of digging through the string, finding the spot where the number is, and removing the characters that make up the string.
Yes that's the way the bullets are created, but they are removed by just doing this:
Code:
bullets = bullets.Replace("|" & bulletNumber & "|", Nothing)
Can I send you the whole code through PM so you can help me better? I think it would be better to me, because I have never made a game and I am only programming VB for two years so mistakes happen.

Last edited by 1246; 04-22-2011 at 06:46 PM.
Reply With Quote
  #8  
Old 04-24-2011, 03:07 PM
AtmaWeapon's Avatar
AtmaWeaponToo many bullets AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Quote:
And I also don't know how it is possible that I get such a low frame rate, being that in a decent First Person Shooter I get ~100 FPS!
Windows Forms was designed in the early 1980s. It was designed to display static layouts (no animation), rectangular controls with no overlap, and had no support for alpha blending. Over the years, rudimentary animation support and alpha blending has been added, but its design focus is still traditional Windows applications made up of buttons, text boxes, etc. that don't move around much as you use the application.

Few commercial games are written in Windows Forms; those that are tend to be turn-based puzzle games such as Minesweeper or games that don't require smooth animation such as Tetris. Windows Forms was not designed to handle motion or high framerates. The most fluid WinForms applications I've seen use 0 controls in favor of drawing everything on a bitmap which is blitted to the screen frequently; this can suffer from flicker but is the best you'll get.

Commercial games used to be written via direct access to the video driver; now they lean on DirectX, OpenGL, or another graphics library that abstracts the hardware away. If you're really interested in writing high-performance games, I would suggest you turn your attention to XNA and learn C# as well since it's more widely used. XNA is a .NET abstraction of DirectX designed for making high-performance 2D and 3D games.

Quote:
Can I send you the whole code through PM so you can help me better?
I would prefer it if you put the code in a zip file (no .exe files, they're against the forum rules!) and attach it to a post. That way other people can look at it as well. I'm a pretty smart guy, but I sometimes say stupid things, I don't know everything, and my free time is often limited. You'll get the best help if you let more people give you advice.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #9  
Old 04-25-2011, 11:07 AM
AtmaWeapon's Avatar
AtmaWeaponToo many bullets AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Perhaps it'd be easier to post an example than to look at your code. Maybe you don't want to give your code away. That's fine. The attached VS 2010 project should be helpful, though I am still curious how you're getting 60 FPS.

I'm talking through it in the order I implemented stuff; I like to build things step by step.

Playfield is the panel that does the drawing. First, note how I calculate framerate and cause the update. _frameTimer is set to an interval of 18ms to try and get 60 FPS. When it ticks, I call Invalidate(). That should get me 60 FPS. CalculateFramerate() is called each frame; it determines the time elapsed since the last frame and calculates what framerate that represents. My OnPaint() method draws the framerate to the screen in an orange font. At the start of the project, I got about 31 FPS. This is why I'm curious about your technique for getting 60 FPS. We'll come back to the playfield.

Moving on, take a look at Bullet. It's got a location, a velocity, a size, and a color. That tells me where to draw it, how to draw it, and how fast it's moving. The velocity is in terms of pixels per frame, which isn't the best choice but it will work for this demo. Back to the playfield.

NumberOfBullets is what it says. When it changes, InitializeBullets is called. What that does is a bit involved and not really important to the demo; all I'll say is it starts with bullets with velocity 1 and fills rows and columns with those bullets, then starts a new "layer" with a higher-velocity bullet. This should make a neat parallax effect once the screen is full of bullets. Note that _bullets stores each bullet. Now back to OnPaint(), under the comment "draw all bullets".

Now to make them move. Every frame, MoveBullets() moves each bullet. Each bullet must have its velocity added to its location. I want them to rain from top to bottom, so I'm only updating the Y index. If a bullet leaves the screen, it's no longer active and thus removed from the list of bullets.

That leaves the form. The text box you can type in represents the number of bullets that will be generated. Change the number and push "Go" to play the animation. The total number of onscreen bullets is polled twice a second and indicated in another text box. Note that the first time the control loads it uses the wrong number of rows and columns due to an oversight I didn't correct.

In my experiments, there was no difference between 5,000 and 10,000 bullets, but somewhere around 25,000 bullets I started losing framerate. 10,000 seems like a ridiculous number of onscreen elements, so I'm satisfied that this works for 30 FPS.

Now you can't tell me my idea doesn't work because it's demonstrable. To get further, you have to identify what's different between your code and mine. Personally I'm still curious how you are getting 60 FPS.

There is one *small* snag: you can't guarantee that WinForms uses the GPU for rendering, so it's possible the rendering will be CPU bound. If you've got a slow machine, you're going to struggle to maintain framerate and throwing a beefier video card at the problem might not work.
Attached Files
File Type: zip ParticleDemo.zip (14.0 KB, 37 views)
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #10  
Old 04-25-2011, 05:41 PM
passel's Avatar
passelToo many bullets passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,026
Default

The resolution of most current machines I've worked on seems to be a maximum clock tick rate of 64hz (about 16.6666 ms).
Since you asked for 18ms ( > than 1 tick), you'll probably getting events every 2 ticks (i.e. about 32 hz).
Try setting your interval to less than 1 tick (say 10ms, which should be 100hz), and you will probably see the 1 tick interval frame rate (about 64 hz) for your test.
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.
Reply With Quote
  #11  
Old 04-26-2011, 09:23 AM
AtmaWeapon's Avatar
AtmaWeaponToo many bullets AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Right, that's a logical explanation. Except it clashes with the documentation:
Quote:
The Windows Forms Timer component is single-threaded, and is limited to an accuracy of 55 milliseconds. If you require a multithreaded timer with greater accuracy, use the Timer class in the System.Timers namespace.
I tried it and got something around 64Hz like you said, but it makes me feel funny to rely on something the documentation explicitly says won't work. Maybe "accuracy" in this situation is different from "resolution"? As in, if I ask for events at 1000ms I can expect them between 945 ms and 1055 ms?

It does explain why in a previous project where I used a threaded timer I couldn't seem to get 60 FPS unless I asked for 120 FPS. I was assuming it was thread marshaling overhead but now it seems more like I can get either 32 FPS or 64 FPS and nothing in between.

It still leaves XNA as a superior choice.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #12  
Old 04-26-2011, 10:34 AM
passel's Avatar
passelToo many bullets passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,026
Default

Unfortunately you can't always rely on the documentation.
The timer "accuracy" is a system dependent "feature" depending on the clock chip used.
I believe the 55 millisecond accuracy is a "guaranteed" minimum, so is safe to state as "fact" on all platforms. The 55 millisecond value is what was available on the original IBM PCs, and what the Timer control provided in VB1 on Windows 3.1.
I was pleased when I was running VB3 on a Windows95 machine and the Timer control on that system provided a 10 millisecond interval, so assumed that a 100hz tick was the new standard.
Later, I discovered the value seems to be capricious, so you can't really count on specific value (but 64hz seems to be pretty common now), except for the 55 millisecond (which probably isn't really true any more, since the clock period is probably some multiple of 15.625 milliseconds that is close to 55 milliseconds, if the tick is 64hz).

Yes, some tool designed to build games would be superior to a native Win Form implementation in most every case.
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.

Last edited by passel; 04-26-2011 at 10:45 AM.
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
Too many bullets
Too many bullets
Too many bullets Too many bullets
Too many bullets
Too many bullets
Too many bullets Too many bullets Too many bullets Too many bullets Too many bullets Too many bullets Too many bullets
Too many bullets
Too many bullets
 
Too many bullets
Too many bullets
 
-->