2D Collision Detection & Drawing on a Backscreen

Just noticed this (about 7 turns into this scenario):
Screenshot 2023-05-12 160645
The two infantry units in the middle are overlapping (I’ve got the corner rects being drawn for debugging).

Do you have any thoughts about what’s going on?

This is the only change I made to your code (other than outlining the rects):

            // Rotate the corners around the center of the rectangle
            // AHA! Our units don't rotate around the center!
            // Vector2 center = new Vector2(rect.Center.X, rect.Center.Y);

            Vector2 center = new Vector2(rect.Center.X, rect.Top + 3);

Can you try to debug the error by rendering the rectangles in green for example if there is a collision? whenever CheckCollision returns true render the rect as green color, otherwise red.

Then after that, rotate each of your rectangles by 0.1 degree or less per update loop so you can double check when a collision happens or not, in that way you can check if it is working or not, I guess there is some offset somewhere that is not counted, so first check what works and what doesn’t. Then we can double check what is missing. It can be that the logic that checks what to do with the collisions may have an error too if all the collisions work as expected, let’s debug first the collision, if it works, then the error is outside.

So, here’s what I’m seeing when I drop in to the debugger: Unit #6 (the infantry unit on the left) is returning true and stopping, however Unit #4 (the unit on the right) is not returning false and advancing (into Unit #6’s space). Here’s a snippet of my code that calls your code to check for a ‘friendly collision’:

        public static bool isFriendlyCollision(bool side, int UnitID, PointI NextPoint)
        {
            bool isCollision = false;
            Rectangle rec1 = new Rectangle();
            Rectangle rec2 = new Rectangle();

            if (side) // blue
            {
                rec1 = CalculateActualRect(BlueArmy[UnitID], NextPoint);

                for (int i = 0; i < BlueArmy.Count; i++)
                {
                    if (i == UnitID)
                        break;

                    rec2 = CalculateActualRect(BlueArmy[i], BlueArmy[i].Location);
                    isCollision = CheckCollision(rec1, (float)BlueArmy[UnitID].Facing, rec2, (float)BlueArmy[i].Facing);

                    if (isCollision)
                        return isCollision;

                } // for i

            } // blue

Any thoughts? I should add that the code seems to be correctly stopping collisions previous to this.

And this is my code for calculating the 4 corners of the unit rect (this is necessary because different units have different sized pieces based on line or column formation and unit type):

  public static Rectangle CalculateActualRect(MATEUnit TheUnit, PointI Location)
        {
            Rectangle ActualCorners = new Rectangle();

            // We have to calculate the actual X1,Y1 X2,Y2 of the unit rect
            // Taking into consideration unit type
            // and unit formation
            // Also, scale by PieceSize

            if (TheUnit.Type == (int) UnitTypes.Supplies ||
                TheUnit.Type == (int) UnitTypes.HeadQuarters ||
                TheUnit.Type == (int) UnitTypes.Artillery ||
                TheUnit.Type == (int) UnitTypes.HorseArtillery) 
            {
                if(TheUnit.Formation == Formations.Column ||
                    TheUnit.Formation == Formations.MeleeColumn)
                {
                    // These are UNSCALED icon sizes
                    // 26 x 44 including arrows
                    // 26 x 33 without arrows

                    ActualCorners = new Rectangle(TheUnit.Location.X - (int) (14 * PieceSize), TheUnit.Location.Y, (int) (26 * PieceSize), (int) (44 * PieceSize));

                } // column formation
                else // line formation
                {
                    // 44 x 36 including arrows
                    // 44 x 25 without arrows

                    ActualCorners = new Rectangle(TheUnit.Location.X - (int) (22 * PieceSize), TheUnit.Location.Y , (int) (44 * PieceSize), (int) (36 * PieceSize));
                }
            } // small unit
            else // large unit
            {
                if (TheUnit.Formation == Formations.Column ||
                    TheUnit.Formation == Formations.MeleeColumn)
                {
                    // 26 x 71 including arrows
                    // 26 x 60 without arrows

                    ActualCorners = new Rectangle(TheUnit.Location.X - (int) (13 * PieceSize), TheUnit.Location.Y , (int) (26 * PieceSize), (int) (71 * PieceSize));

                } // column formation
                else // line formation
                {
                    // 62 x 38 including arrows
                    // 62 x 27 without arrows

                    ActualCorners = new Rectangle(TheUnit.Location.X - (int) (31 * PieceSize), TheUnit.Location.Y , (int) (62 * PieceSize), (int) (38 * PieceSize));


                }
            } // large unit

            // put a little air around the rect
            ActualCorners.X = ActualCorners.X - 4;
            ActualCorners.Width = ActualCorners.Width + 8;
            ActualCorners.Y = ActualCorners.Y - 2;
            ActualCorners.Height = ActualCorners.Height + 2;

            return ActualCorners;

        } // CalculateActualRect

I think there may be a problem before

isFriendlyCollision

call, I think both blue units are moving at the same time right? would it be possible for you to put a break point when unit 4 moves on top of the other blue unit? so you can check at that time if there is any collision or not. The easy way to test that scenario if you don’t have the whole scenario to repeat, you can just put the 2 blue units and see what happens.

It looks like collision is ok, but I do not know if something else happens outside the validation loop.

is the variable facing the angle in which the unit is looking at?

Unit[n].facing is the direction the unit is facing and is also identical to float rotation in the Draw():

                        spriteBatch.Draw(UnitImage, new Vector2(
                            DrawPoint.X + LeftMapOffset,
                            DrawPoint.Y + TopMapOffset), null,
                                   Color.White * Opacity, // opacity is 100%
                                   (float)BlueArmy[i].Facing, origin,
                                   (float)PieceSize, SpriteEffects.None, 0f);

Units do not actually move simultaneously. Who moves first (blue / red) is switched every other turn, but then the units are moved in order of their ID# so, unit #4 moves before unit #6. When Unit #4 is moving, it does not trip isFriendlyCollision.

One of the nice things is that I have a ‘replay’ function (takes a screencap every turn) built into the game. Here’s what’s going on, step by step:

Turn 8

Turn 9

Turn 10

Turn 11

Turn 12

Turn 13
Unit #2 does not trip isFriendlyContact ever. A clue?

do units move in small steps or big ones? are you able to check the 4 rectangle vertices of unit 2 and unit 6? in screen coordinates

I would assume you are somehow doing the collision checks with information not yet updated to the current frame … have you checked that this is not the case?

(The actual render output may not reflect this properly as rendering happens when all updates are done anyway, which may not be the case during actual unit movement)

Units are checked via small steps. Every unit has has a List<> of contiguous points to its objective:

   PointI StartingPt = new PointI(BlueArmy[i].Commands[0].Path2Objective[0].X, BlueArmy[i].Commands[0].Path2Objective[0].Y);
                

And then decremented as it moves:

 BlueArmy[i].Commands[j].Path2Objective.RemoveAt(0);

So, we’re not ‘jumping over’ any point; though we could be moving diagonally.

That is correct. We get the next intended point, check it for collisions, and only then move.

This jumps out at me in your friendly collision method
If (i == UnitID)
break;

If im reading it correctly, that line is meant to make sure your not checking collision against itself. However if it gets to itself in your friendly units list then it breaks out of the collision detection and not checking any units after in yourvlist

Should this instead be,
if i does not equal unitid - creat actual rectangle, check collision etc.

boot,

Yes, that’s the mistake you are right

the code should be

if (i==UnitID){
   continue;    // <--- Not break;
}

when you do a break, you skip the collision for all the other unit ids after your id number, when you do continue you only skip your own id.

Great!

It generally ends up being something so obvious in hindsight when trying to find a bug

Absolutely correct!

I so miss an extra pair of eyes and working with a partner.
Thanks you, two!

Absolutely correct! A+!

I made a far simpler game years ago - and opted for a two-phased approach to collision detection. Firstly, I used a point inside a rectangle for a proximity collision detection. I was not expecting many things to get so close that I’d want the expensive 2D pixel collision detection routine.

The 2D collision detection was done using bit-masks, where I had 8 pixels represented by a byte, where 1 represented a pixel in the graphic, and 0 was empty space, and not part of the collision detection.
I had 2 collision masks - 1 for each of the two game objects I determined had potentially collided in the first step.
I realised that using binary operations, I could AND two bytes together, and if the resulting value was greater than 0, then 2 or more pixels must overlapped, and the collision detection routine ended at that point.
I further enhanced the code to consider 1 x byte, 2 x bytes, 3, and 4 x bytes. I could therefore use a UInt32 and check for 32 pixel collisions in a single test. Given my sprites were about 32 x 32, you can see that bit-shifting (64bit ulong) values and ANDing them together was hard to get right in my head, but basically, this was the kind of checking I’d have done on the Commodore Amiga decades ago.

I’ve never put that routine through a test to find out how many I could do. But basically, I didn’t have a back-buffer. I just had sprite data I drew on screen, and collision masks.

My routine is limited though - as it won’t consider rotation. So writing a rotate routine, and then a collision based on the rotated collision masks would be the missing step in my case, had I a need for such a collision detection.

In terms of performance, none of my collision routine had anything to do with the GPU - it was all in Ram. Given the small amount of memory per collision mask, and given all collision masks were pretty much kept in the same part of memory (array) then cache runs in the cpu would suggest massive performance potential (in my mind).

That really sounds like some of the old school stuff we did on the 68000 chips (Atari, Mac, Amiga). I was actually regaling my wife earlier about how great the 68000 was and she just rolled her eyes and went back to her drink.

1 Like