Home Register Calendar Today's Posts FAQ Free Publications Search
 > 2D Platform Game: Collisions

#1
03-31-2009, 12:55 AM
 Joe_Dert Freshman Join Date: Mar 2009 Posts: 26
2D Platform Game: Collisions

 Alright, I'm making a game engine, its coming along pretty nicely, but I'm having trouble with collision detection.. This will be a platform game.. I'm using GDI+ for my rendering. My rendering engine, basically draws upon a sprite class I wrote, this class is getting pumped into a collection, then I'm using a for loop to draw the images in the collection.. Code:  Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint Try For i As Integer = 1 To SCollection.Count Step 1 Dim Image As New Bitmap(DirectCast(SCollection.Item(i), clsSprite).Filename) Image.MakeTransparent(DirectCast(SCollection.Item(i), clsSprite).TColor(Image)) e.Graphics.DrawImage(New Bitmap(Image), DirectCast(SCollection.Item(i), clsSprite).Pos.X, DirectCast(SCollection.Item(i), clsSprite).Pos.Y, DirectCast(SCollection.Item(i), clsSprite).Width, DirectCast(SCollection.Item(i), clsSprite).Height) Next Catch End Try End Sub At this point, I have my rendering engine working pretty well, and I have a fully working character, as far as movement goes, he runs, he jumps, etc,.. (This is all animated btw..) So seeing the above code, you can probably see how I intend to handle collision, it will be based on the sprite class, using its built in parameters to tell where my objects are, it works pretty well, except I think my collision detection is flawed, or I'm not applying it correctly, or it simply can't keep up. I'm using a timer for my game loop, it is running at 1ms intervals, all collision detection is handled there.. The code below is called in a for loop, using the same collection method I use for rendering, right now it just checks the player against any objects I have loaded. (I suck at math, so I borrowed the collision code from a tutorial, you may recognize it, ThePentiumGuy wrote it..) Code:  Public Function Collided(ByVal character As clsSprite, ByVal otherobject As clsSprite) As Boolean a = Math.Abs(character.CenterX - otherobject.CenterX) b = Math.Abs(character.CenterY - otherobject.CenterY) hypoteneuse = Math.Sqrt(a ^ 2 + b ^ 2) If hypoteneuse <= (character.Width + otherobject.Width) / 2 Or hypoteneuse <= (character.Height + otherobject.Height) / 2 Then Return True Else Return False End If End Function Note: Some of these properties are defined in my clsSprite... So can you guys help me figure this out, I need rectangle based collision detection, that works from any angle(Left, Right, Top, Bottom), and will work with things like gravity..(Should gravity be constant, or only applied when needed?) I basically need help working out the kinks in the formula so it can detect collisions from any direction, since this will be handling almost all my collisions. (Character, Object, Projectile, etc,..) If you need any other info just ask.. Any tips would be welcome, this is my first game, so I'm pretty new to this..

Last edited by Joe_Dert; 03-31-2009 at 01:05 AM.
#2
03-31-2009, 02:38 AM
 Rockoon Joseph Koss * Guru * Join Date: Aug 2003 Location: Unfashionable End Posts: 3,615

 Your Collided() function does not test for something that can be defined as a collision of axis aligned squares/rectangles, but instead is some sort of hybrid circle collision tester. Define, in english, what it means for axis aligned squares to be colliding (hint: it has nothing to do with pythagoras) .. and the answer you are seeking should be fairly obvious from that.
#3
03-31-2009, 03:28 AM
 Joe_Dert Freshman Join Date: Mar 2009 Posts: 26

 I don't really follow, I suck at riddles, and math.. In English for two items to collide, means they hit each other.. That doesn't help a non-mathematician type like me at all.. Sorry.. Edit: Are you talking about checking for overlaps.. ? Basically... This type of deal.. Code: if P1.Pos.X + P1.Width >= P2.Pos.X Then end if

Last edited by Joe_Dert; 03-31-2009 at 04:02 AM.
#4
03-31-2009, 07:26 AM
 Rockoon Joseph Koss * Guru * Join Date: Aug 2003 Location: Unfashionable End Posts: 3,615

Quote:
 In English for two items to collide, means they hit each other..
Both of your descriptions of the problem takes for granted the great body of knowledge and experience that humans have. Thats great if you are telling a human what you want to do, but not great for telling a computer what you want to do.

Quote:
 Edit: Are you talking about checking for overlaps.. ?
What does it actualy mean to check for overlaps given the data you have?
#5
03-31-2009, 07:49 AM
 darkforcesjedi Trust me, I'm an * Expert * Join Date: Apr 2001 Location: In ur base, pwnin d00dz Posts: 1,964

Quote:
 Originally Posted by Joe_Dert Code:  Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint Try For i As Integer = 1 To SCollection.Count Step 1 Dim Image As New Bitmap(DirectCast(SCollection.Item(i), clsSprite).Filename) Image.MakeTransparent(DirectCast(SCollection.Item(i), clsSprite).TColor(Image)) e.Graphics.DrawImage(New Bitmap(Image), _ DirectCast(SCollection.Item(i), clsSprite).Pos.X, _ DirectCast(SCollection.Item(i), clsSprite).Pos.Y, _ DirectCast(SCollection.Item(i), clsSprite).Width, _ DirectCast(SCollection.Item(i), clsSprite).Height) Next Catch End Try End Sub
What is this ludicrousness??? Reading images from the hard disk every time you want to display them is slow. Very slow. Furthermore, you create a copy of every image after loading it but before displaying it. This is nonsensical.

What is SCollection declared as? You perform 6 casts on SCollection.Item(i) and every one of them is probably unnecessary.

Cache the images in the clsSprite class instead of just the filenames. Load and process the images only once (on class initialization). Store the sprites in a generic list or collection so you don't cast them every time you want to use them.

It looks like you're probably using a different image file for each image frame. This is inefficient and uses more resources than a sprite sheet. I've got a bunch of posts on here about drawing/animating sprites. Look through my post history.
__________________
To err is human; to debug, divine.
#6
03-31-2009, 08:35 AM
 Joe_Dert Freshman Join Date: Mar 2009 Posts: 26

Quote:
 Originally Posted by darkforcesjedi What is this ludicrousness??? Reading images from the hard disk every time you want to display them is slow. Very slow. Furthermore, you create a copy of every image after loading it but before displaying it. This is nonsensical. What is SCollection declared as? You perform 6 casts on SCollection.Item(i) and every one of them is probably unnecessary. Cache the images in the clsSprite class instead of just the filenames. Load and process the images only once (on class initialization). Store the sprites in a generic list or collection so you don't cast them every time you want to use them. It looks like you're probably using a different image file for each image frame. This is inefficient and uses more resources than a sprite sheet. I've got a bunch of posts on here about drawing/animating sprites. Look through my post history.
I haven't had any problems with reading files as of yet, then again I have a 10,000RPM Sata2 HD, so since it moves like 30+ mb\sec, I doubt a 1.2kb file will give it much trouble.. I would be more inclined to agree if I was loading several multiple megabyte images..

SCollection is a collection, the DirectCast allows you to access the properties of a collection... I don't know what you're on about here.. This from my understanding makes it run faster, since it doesn't have to figure out the properties itself at run time.. It also enables intellisense to detect these properties, which makes coding it a little easier..

As for the image code itself, it was taken straight from a tutorial by ThePentiumGuy, so if you have a problem with that particular image loading code, take it up with him,. I am assuming you mean this bit "e.Graphics.DrawImage(New Bitmap(Image),"...

I did make a few modifications so it could work with Collections of clsSprite..

And finally, loading all the image resources at load time as you seem to be suggesting, would be more resource heavy in the long run, if I made a huge game with lots of resources, that would probably run like crap, or at least hog a lot more memory than necessary considering how memory heavy .Net is anyways..

Considering I'm only going to have maybe 20 images on screen at most, it seems better to load and unload on the fly,.

Last edited by Joe_Dert; 03-31-2009 at 08:44 AM.
#7
03-31-2009, 09:27 AM
 darkforcesjedi Trust me, I'm an * Expert * Join Date: Apr 2001 Location: In ur base, pwnin d00dz Posts: 1,964

Quote:
My post was not meant to be an attack on you. It was meant to be more facetious than egregious.

The problem with loading all the images from the hard disk every time the form paints is not so much the hard disk throughput. But every time you load a (presumably compressed) image, the Bitmap class has to process the header, allocate memory, and decompress the image. This process is "slow". How slow? In your case probably not very noticeably until you try to redraw many times per second (drag another window across your form while it's animating).

Your method seriously limits the amount of graphic throughput your application can have. Also, take note that you're not disposing any of the bitmaps you create, which means they will sit in memory until the garbage collector decides to dispose them. If you're concerned about memory footprint, you should dispose these images when you're done with them.

On the topic of collections. Take a look at the Generic List class. If you create a List(Of clsSprite) (in the System.Collections.Generic namespace), then every item in the list is necessarily a clsSprite and no casting is necessary.

Quote:
 Originally Posted by Joe_Dert And finally, loading all the image resources at load time as you seem to be suggesting, would be more resource heavy in the long run, if I made a huge game with lots of resources, that would probably run like crap, or at least hog a lot more memory than necessary considering how memory heavy .Net is anyways..
This is contrary to reality. Performing all image processing ahead of time once saves a tremendous amount of CPU time over doing it every time for every image you draw. It absolutely would use more memory, you're right about that. But the trade-off is the ability to draw the image tens or hundreds of times faster. I'm not the only one that does it this way. When you're playing a commercial game and you see a "loading" screen, it's doing the same thing (preprocessing and caching data for faster access).

Keep in mind that not all sample code on the internet is of the same quality. If you want to do it that way, go right ahead.

As for the collision detection. Try the IntersectsWith method of the Rectangle class.
__________________
To err is human; to debug, divine.
#8
03-31-2009, 10:29 AM
 Joe_Dert Freshman Join Date: Mar 2009 Posts: 26

Quote:
Ludicrous is a bit extreme a term to be using when talking about ones code..

But its cool.

I still disagree about the loading, most commercial games pre-load the level, not the whole game, it takes so long because they are loading like 400-1500mb of data.. Which is a small portion when you consider most modern games have nearing up on 15gigs of data, in compressed formats..

We're however talking about platform games, those things we used to play on the NES which was a pretty weak system, I run GTA4 relatively well on my system, if it can't handle this game, I'll be amazed, and stupefied, and then amazed some more..

Btw, DirectCast, it tells the program exactly what type of object its dealing with, thus avoiding the run time overhead, Microsoft uses this a lot in their VB code, and in their examples on the MSDN, so most the VB.Net functionality you know and love uses this very same method behind the scenes.

You were right about the images not being disposed of properly, I just never tested it, sometimes dispose likes to flush what you're working with completely, but it seems images stay loaded even if you dispose of them after you are done.. So I've made that change, and removed the double declare of new bitmap.. Lets just hope the garbage collector cleans up the rest.. I really wanted to use an independent render method, but I couldn't get it working with .gif images, something about indexed images, or some such, so I went with an OnPaint method..

Anyways, how would I go about using Rectangle, like Dim Player As Rectangle, then define my size\pos, etc.. ? If so I should be able to modify my class to use it without much work...

Last edited by Joe_Dert; 03-31-2009 at 10:45 AM.
#9
03-31-2009, 10:59 AM
 darkforcesjedi Trust me, I'm an * Expert * Join Date: Apr 2001 Location: In ur base, pwnin d00dz Posts: 1,964

 Casting of object is rarely necessary if you know what type of objects you're dealing with. Take a look at this alternative: Code: Dim MySprites As New List(Of clsSprite) Sub LoadSprites() Dim S As clsSprite ' perform initialization stuff For i As Integer = 1 To NumberOfSprites S = New clsSprite ' finish initializing sprite MySprites.Add(S) Next End Sub Sub DrawSprites(Surface As Graphics) ' No casting necessary because members of MySprites are declared as clsSprite For Each S As clsSprite in MySprites If System.IO.File.Exists(S.FileName) Then Using B As Bitmap = Bitmap.FromFile(S.FileName) ' Do processing you want with bitmap ' Draw bitmap to surface Surface.DrawImage(B, S.Pos.X, S.Pos.Y, S.Width, S.Height) ' Using block will dispose of Bitmap object automatically at the end of the block End Using End If Next End Sub Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint DrawSprites(e.Graphics) End Sub To create a rectangle for your sprite, add a property of type rectangle: Code: ReadOnly Property Rect() As Rectangle Get Return New Rectangle(X, Y, Width, Height) End Get End Property Then you can test for collisions like: Code: ' Here I'm assuming MySprites is declared as a ' List(Of clsSprite) as above with the Rect ' property added For i As Integer = 0 To MySprites.Count - 1 For j As Integer = i + 1 To MySprites.Count - 1 If MySprites(i).Rect.IntersectsWith(MySprites(j).Rect) Then ' Sprites i and j have overlapping rectangles End If Next Next
__________________
To err is human; to debug, divine.
#10
03-31-2009, 11:20 AM
 Joe_Dert Freshman Join Date: Mar 2009 Posts: 26

Quote:
 Originally Posted by darkforcesjedi Casting of object is rarely necessary if you know what type of objects you're dealing with. Take a look at this alternative: Code: Dim MySprites As New List(Of clsSprite) Sub LoadSprites() Dim S As clsSprite ' perform initialization stuff For i As Integer = 1 To NumberOfSprites S = New clsSprite ' finish initializing sprite MySprites.Add(S) Next End Sub Sub DrawSprites(Surface As Graphics) ' No casting necessary because members of MySprites are declared as clsSprite For Each S As clsSprite in MySprites If System.IO.File.Exists(S.FileName) Then Using B As Bitmap = Bitmap.FromFile(S.FileName) ' Do processing you want with bitmap ' Draw bitmap to surface Surface.DrawImage(B, S.Pos.X, S.Pos.Y, S.Width, S.Height) ' Using block will dispose of Bitmap object automatically at the end of the block End Using End If Next End Sub Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint DrawSprites(e.Graphics) End Sub To create a rectangle for your sprite, add a property of type rectangle: Code: ReadOnly Property Rect() As Rectangle Get Return New Rectangle(X, Y, Width, Height) End Get End Property Then you can test for collisions like: Code: ' Here I'm assuming MySprites is declared as a ' List(Of clsSprite) as above with the Rect ' property added For i As Integer = 0 To MySprites.Count - 1 For j As Integer = i + 1 To MySprites.Count - 1 If MySprites(i).Rect.IntersectsWith(MySprites(j).Rect) Then ' Sprites i and j have overlapping rectangles End If Next Next
I get what you're saying about casting, I rarely use it, but, collections don't know what type of object they have in them, so at run time it adds a ton of overhead figuring out what type of object its holding, and then fetching its properties if you call any, so in this case, a directcast let it know that it held clsSprite objects..

One cool thing about collections, is you can supply a name for an object when you add it, then, you don't need to know where the object is in the collection to access it, you can just call it by name, and access any of its properties. That's one of the reasons I wanted to try them out..

MyHeight = SCollection.Item("SCPlayer").Height ' You can access it like this..

Anyways, thanks for the code, and for your help, I will definitely try to implement some of it, a lot useful stuff from what I see, I didn't know about a few things you did there, like the Using bit, etc,..
#11
03-31-2009, 11:51 AM
 darkforcesjedi Trust me, I'm an * Expert * Join Date: Apr 2001 Location: In ur base, pwnin d00dz Posts: 1,964

Quote:
 Originally Posted by Joe_Dert I get what you're saying about casting, I rarely use it, but, collections don't know what type of object they have in them, so at run time it adds a ton of overhead figuring out what type of object its holding, and then fetching its properties if you call any, so in this case, a directcast let it know that it held clsSprite objects..
The problem is you're using a collection. A "List(Of clsSprite)" knows what it contains. No casting is necessary on its contents to retrieve them as clsSprite.

If you want to give your objects names, take a look at the generic System.Collections.Generic.Dictionary class. The Dictionary is far more flexible than a collection because it allow you to associate an object of one fixed type (not necessarily a string) with an object of any other type. You can have a dictionary that maps integers to strings, controls to other controls, classes to integers, or basically anything.

A Dictionary(Of String, clsSprite) would allow you to do the same thing as a collection, but without any casting:
Code:
Dim MySprites As New Dictionary(Of String, clsSprite)
MySprites("Grilled Cheese Sandwich").Height = 2
Even if you choose not to use generics in your current project, they're definitely something you should have in your arsenal for future projects.
__________________
To err is human; to debug, divine.
#12
03-31-2009, 12:16 PM
 Joe_Dert Freshman Join Date: Mar 2009 Posts: 26

Quote:
 Originally Posted by darkforcesjedi The problem is you're using a collection. A "List(Of clsSprite)" knows what it contains. No casting is necessary on its contents to retrieve them as clsSprite. If you want to give your objects names, take a look at the generic System.Collections.Generic.Dictionary class. The Dictionary is far more flexible than a collection because it allow you to associate an object of one fixed type (not necessarily a string) with an object of any other type. You can have a dictionary that maps integers to strings, controls to other controls, classes to integers, or basically anything. A Dictionary(Of String, clsSprite) would allow you to do the same thing as a collection, but without any casting: Code: Dim MySprites As New Dictionary(Of String, clsSprite) MySprites.Add("Grilled Cheese Sandwich", New clsSprite) MySprites("Grilled Cheese Sandwich").Height = 2 Even if you choose not to use generics in your current project, they're definitely something you should have in your arsenal for future projects.
Hmm, well, I'll look into it, as you said, its always good to expand your programming knowledge, to be honest that's the main point of this project.

Game creation uses a bit of everything so I figured it would make a for decent challenge, and would force me to learn things that I'd been avoiding..
#13
03-31-2009, 07:05 PM
 Rockoon Joseph Koss * Guru * Join Date: Aug 2003 Location: Unfashionable End Posts: 3,615

#14
04-01-2009, 01:24 AM
 Joe_Dert Freshman Join Date: Mar 2009 Posts: 26

Quote:
 Many programmers have found that the set of abstraction: left edge, right edge, top edge, bottom edge ..is a really simple one for solving the problem of testing for the intersection of two axis aligned squares.
That would be a test to see if they overlap each other, just as I had said in my first reply to this question..

I used a different term than you expected, but it means the same thing as far as I can tell. (Overlap, occupy the same space, within each others boundaries, etc, etc,..)

I fail to see how this makes me unable to think like a programmer..

Especially considering I already knew about that particular method, I was merely seeking a more professional way of doing it.. Apparently, that's about the best there is, IntersectsWith probably uses the same method its just hidden within the Rectangle class..

Well, I hope you don't take this post as being hostile, I'm not trying to be, I'm mainly just tired..

Anyways, I just added in DirectInput, and DirectSound, I sort of regret using DirectInput, I had to rewrite a bunch of code, and for basically the same effect, but it will make adding in gamepad support easier, if I decide to add it that is..

I had an issue with DSound as well, I tried playing my footstep sound, and it gave me a buffer too small exception, which doesn't make sense, considering I loaded much larger files into the same buffer without problems, I think it meant my file was too small, its a very short "chi" sound..

I'm also trying to get my jump animation to look right, jumps are probably ten times harder than walking, dashing, crouching, etc,.. I have my height\distance where I want them, now I just need to slow down the interval between image changes, and get the timing right for the landing..

I have my collision detection partially working, I don't fall through the floor under constant gravity, and I can jump on platforms, or any other object I render.. But I still need to add in left\right side collision behavior, and bottom as well..

After that, I will probably start working on getting my world to scroll, and then populating it with platforms, etc,.. Then I have to change it all over to use a level loading system, right now I'm just directly loading everything, I want to load from a .lev file, and have it fetch the proper assets for each level..

Well anyways, feel free to drop any tips along those lines, if you know any good tutorials that would help, or anything like that.. As I said this is my first game, so I haven't done most of this before.

Last edited by Joe_Dert; 04-01-2009 at 01:30 PM.

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

 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 Rules
 Forum Jump User Control Panel Private Messages Subscriptions Who's Online Search Forums Forums Home News & Notifications     Announcements     .Net News & Articles Visual Basic .NET (2002-2015), including Express editions)     .NET Knowledge Base         Tutors' Corner         Code Library     ASP.Net     .NET Communications     .NET Game Programming         Managed DirectX     .NET Office Automation     .NET File I/O and Registry     .NET Database and Reporting     .NET Interface and Graphics     .NET Installation / Documentation     .NET General     Xtreme .NET Talk Legacy Visual Basic (VB 4/5/6)     Knowledge Base         Tutors' Corner         Code Library     Communications     Game Programming         DirectX     VBA / Office Integration         Excel         Word, PowerPoint, Outlook, and Other Office Products     API     File I/O and Registry     Database and Reporting     Interface and Graphics     Installation / Documentation     General Other Languages     Web Programming     Miscellaneous Languages General Discussion     Member Offers and Deals     Tech Discussions     Random Thoughts     Forum Questions / Concerns / Comments