Author Topic: Precise collision coordinate  (Read 3770 times)

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Precise collision coordinate
« on: 2009-10-26, 07:32:35 AM »
Hi guys,

Is it possible to have a precise coordinate of a collision between 2 sprites?  A quick explanation: I spawn pixels in my game when there is an impact with the sword.  Blood, water, sparks, chip of bones, etc.  (there only pixels, but that's what they are supposed to be).  But they don't spawn at the location of an impact.  So I figured I would need 2 custom functions: a testCollisionMask that returns a coordinate where the impact happened and a add sprite function that receives a coordinate...

Do you have any suggestion of examples I could use to achieve this?  Is there already a way to do this?

Thanks!  ;D
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Precise collision coordinate
« Reply #1 on: 2009-10-26, 10:12:10 AM »
1) Have you considered the possibility that a collision may occur on multiple pixels at once?
2) The function that tests for collisions does not currently know which pixel caused the collision because it tests 32 or 8 bits at a time (I forget which) using a bitwise AND operation on shifted bit masks.  However, it wouldn't too hard to loop through the bits and find the first place (or all places) where "solid" bits overlap.
3) To accomplish this you'll need to follow the collision function to its implementation where it's actually overlapping and testing the collision masks.  It won't be a quick simple thing where you return information that is already available, but just wasn't being used.  I don't have the code handy, but if you can find the location where the implementation of the collision function returns true (a collision occurred), you can add some code near there to determine exactly where the collision occurred and store or return that information somehow.  You may want to do this in a separate implementation of the function so that your other collision detection that doesn't care exactly where the collision occurred doesn't have to deal with the overhead of locating the exact pixel(s).

If you need help with this, let me know and I can investigate it in more detail later when I have the code handy.

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Precise collision coordinate
« Reply #2 on: 2009-10-26, 10:17:36 AM »
1) Yup I did.  In this case, the ideal solution would be the return the center-most coordinate of the collision.  But any coordinate that collides could be good enough too.
2) Ok, so not a walk in the park.  :P
3) Ok, I'll make sure to make another function rather than modifying the one already there.  If I decided to go on with this feature.  I'm not sure I will, I may try to get a "near-enough" result just by using sprite size and location.

I'll keep you informed of my progress.

Thanks! :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Precise collision coordinate
« Reply #3 on: 2009-11-08, 03:31:04 PM »
Hi! 

Okay so I followed the code up to the actual collision detection and I must admit that I am clueless.  I don't understand the method used to calculate a collision.  I made a copy of the collision detection function and rather than having it return a bool, it returns a List<Point>.  With the List<Point>, the calling functions iterates through the list and find the center most coordinate.  It stores it in fields I added in the SpriteBase class.  I didn't start coding a "AddSpriteAtPoint" function yet.

Okay, so I don't understand how I can determine the location of the impact with the mothod used in the code.  Here's the significant portion of the code, where it iterates through the pixels I suppose:

Code: [Select]
      List<Point> result = new List<Point>();  //my List of Points
      [...]
      //the loop that I really don't understand!  :P
      for(int y=0; y < maxY; y++)
      {
         for(int x=0; x < maxX; x+=32)
         {
            int myColIdx = (int)((x+myMinX)/32);
            int myColOff = myMinX % 32;
            int targetColIdx = (int)((x+targetMinX)/32);
            int targetColOff = targetMinX % 32;
            int myMask = m_Mask[y+myMinY,myColIdx] << myColOff;
            int targetMask = target.m_Mask[y+targetMinY,targetColIdx] << targetColOff;
            if (myColOff != 0)
            {
               if (myColIdx + 1 < m_Mask.GetUpperBound(1))
                  myMask |= (m_Mask[y+myMinY,myColIdx+1] >> (32-myColOff)) &
                     ~(unchecked((int)0x80000000) >> (31-myColOff));
            }
            else if (targetColOff != 0)
            {
               if (targetColIdx + 1 < target.m_Mask.GetUpperBound(1))
                  targetMask |= (target.m_Mask[y+targetMinY,targetColIdx+1] >> (32-targetColOff)) &
                     ~(unchecked((int)0x80000000) >> (31-targetColOff));
            }
            if ((myMask & targetMask) != 0)
            {
               //Collision found -> must add points to list rather than return true
               //return true;

            }
         }
      }


The name of the original function is "TestCollisionWith" located in the CollisionMask class.

The idea here is to add all points where the collision occur and then return the list.  If there is no collision, then an empty list is returned.

Can someone give me a hand?  Can someone explain me what this loop is doing and how it works?

Thanks a lot! :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

durnurd

  • Lead Lemming
  • Expert
  • Fanatic
  • *****
  • Posts: 1234
  • Games completed so far: 0
    • MSN Messenger - durnurd@hotmail.com
    • View Profile
    • Find My Ed
Re: Precise collision coordinate
« Reply #4 on: 2009-11-08, 09:48:47 PM »
All right, we'll both be figuring this one out at the same time.  First off, a quick explanation of m_Mask:

m_Mask is a 2-dimensional array of ints:
  int[,] m_Mask.

Each entry in the array represents (up to) 32 pixels horizontally of the collision mask for that sprite.  So, for example, if your sprite is 64x64, then the dimensions of the mask would be [64,2].  That's 64 rows and 2 columns.  An int takes up 32 bits of memory, so it's being used here as a bitmap.  Wherever there's a 1, it's a solid pixel.  If it's 0, it's not solid.  So for a 64x64 sprite that was completely solid (a big ol' block), each bit of each entry in the array would be set to 1.

To simplify:  imagine that m_Mask is instead an array of bits.  It's size is [64,64], and each entry represent a pixel within the sprite.  1 means solid, 0 means empty.  The only difference when using ints is that you're grouping horizontal sets of pixels together in sets of 32, for faster calculation later, using the binary & operator.

Quick side trip:  The binary & operator compares two integers and returns a result where each bit is set to 1 if and only if both of the inputs had that bit also set to 1.  So, for example 1011 & 1110 = 1010.  Basically, it finds those bits that are common between the two inputs.

So, when you have two masks of two different sprite, and you know where there rectangles overlap, you can find which entries in the m_Mask array to compare, get the correct offset within those entries (unless the overlap is directly on a 32-bit boundary) then use the binary & operator.  If the value returned is not 0, then at least one bit is set in the same place of each mask, ergo, those pixels overlap (a collision!)  Let's go through the code and I'll try to explain it.

Code: [Select]
      //By this time, the collision rectangle, that is, the intersection rectangle of the bounds of each sprite, has been calculated.
      //Loop through each row of the collision rectangle
      for(int y=0; y < maxY; y++) {
         //Loop through each column of the collision rectangle, 32 columns at a time
         for(int x=0; x < maxX; x+=32) {
            //myMinX is the leftmost edge of the intersection rectangle relative to this sprite.  We want to find out which int to look at horizontally by
            //determining how many multiples of 32 (x+myMinX) is equal to.  So when we access m_Mask, we'll be using m_Mask[??,myColIdx]
            int myColIdx = (int)((x+myMinX)/32);
            //myColOff determines the offset of the entry in m_Mask where myMinX points to.  If myMinX is a multiple of 32, then the offset is 0.  If
            //myMinX is, for example, 34 or 66, then the offset is 2.  This is the first bit in the mask we want to compare.
            int myColOff = myMinX % 32;
            //The following calculates the same information for the other sprite.
            int targetColIdx = (int)((x+targetMinX)/32);
            int targetColOff = targetMinX % 32;
            //Now we access the actual mask, and shift it by the offset.
            int myMask = m_Mask[y+myMinY,myColIdx] << myColOff;
            //Same as above, for the other sprite.
            int targetMask = target.m_Mask[y+targetMinY,targetColIdx] << targetColOff;

            //The following is magic.  You are not expected to understand.
            if (myColOff != 0)
            {
               if (myColIdx + 1 < m_Mask.GetUpperBound(1))
                  myMask |= (m_Mask[y+myMinY,myColIdx+1] >> (32-myColOff)) &
                     ~(unchecked((int)0x80000000) >> (31-myColOff));
            }
            else if (targetColOff != 0)
            {
               if (targetColIdx + 1 < target.m_Mask.GetUpperBound(1))
                  targetMask |= (target.m_Mask[y+targetMinY,targetColIdx+1] >> (32-targetColOff)) &
                     ~(unchecked((int)0x80000000) >> (31-targetColOff));
            }

            //Now, myMask and targetMask represent the same location in space, for each sprite.  Now, we check if they overlap, using the binary & operator.
            if ((myMask & targetMask) != 0) {
                //Okay, they overlap, since they have at least one bit in common.
            }
         }
      }


So we need to modify that last bit to just find out which bits are common, add the x and y offsets to those bits, and we should technically be done:

Code: [Select]
int overlap = myMask & targetMask;
if (overlap != 0) {
  int pY= y;
  for (int i = 0; i < 32; ++i) {
    if ((overlap & 1 << i) != 0) {
      //This is a pixel overlap.
      int pX = x + myMinX + i;
      result.Add(new Point(pX,pY));
    }
  }
}

That's almost right.  I haven't tested it, and right now, the return values are in sprite-local coordinates.  If you need it in global coordinates, you'll need to add the offset of the sprite to pX and pY before adding it to result.  Please let me know if this is total garbage and I'm way off.
Edward Dassmesser

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Precise collision coordinate
« Reply #5 on: 2009-11-09, 06:33:09 AM »
I suggest that you don't get a list of points, but only remember up to 4 pixels: the top-most pixel, the bottom-most pixel, the leftmost pixel and the right-most pixel.  And if any of these are the same pixel, eliminate them from the list so you don't have duplicates.  I think this will improve performance and still provide a good visual representation of where the collision occurred.

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Precise collision coordinate
« Reply #6 on: 2009-11-09, 01:01:38 PM »
@durnurd:

Thanks a lot for explaining this to me! 

I'm a complete stranger with it comes to working directly with bits.  I always worked with upper-level programming languages like Delphi, C# and scripting languages like Javascript, ActionScript, etc.  I never took programming classes at school and never learned C or C++ (which definitely is a problem sometimes).  So when i encounter bit operations or pointers or manual memory management I'm completely lost.

That being said, I never would have imagined to use a int32 to compare pixels.  It does make sense now that you explain it to me!  Thanks for the quick side trip too, I didn't know what a binary & operation was.  Thanks for commenting the code line by line too, it's a great help!  I definitely like the "magical part".  :P

I'm going to try your suggested code and adjust it with the offset to get global coordinates.  I'll keep you informed on how it goes.

Thanks again!
« Last Edit: 2009-11-09, 01:10:48 PM by Vincent »
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Precise collision coordinate
« Reply #7 on: 2009-11-10, 06:14:53 AM »
Glad to see that, except for the magical part, my code was readable enough to be commented by someone other than myself! :)
I'm not sure, but a brief look at the code suggests to me that the intention of the magical code was to merge two Int32 values from one sprite's mask into a value that can be compared to 1 Int32 from the other mask (in case the sprites are not offset by a multiple of 32 pixels, which is usually the case).

durnurd

  • Lead Lemming
  • Expert
  • Fanatic
  • *****
  • Posts: 1234
  • Games completed so far: 0
    • MSN Messenger - durnurd@hotmail.com
    • View Profile
    • Find My Ed
Re: Precise collision coordinate
« Reply #8 on: 2009-11-10, 08:47:05 AM »
I'd just like to link to this explanation of my comment.
Edward Dassmesser

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Precise collision coordinate
« Reply #9 on: 2009-11-15, 01:02:25 PM »
Great, I added the code this morning.  It works fine but maybe there is something amiss because it's not perfectly in the middle of the collision areas.  Maybe I did something wrong with the offset.  Anyway, it's good enough as it is.

If you want to look at it, it is in the new uploaded version of the game in the repository.

Thanks again!  ;D
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com