Search code examples
c#2dmonogameside-scroller

Monogame Basic Collision Detection Fails to Work


I have a weird issue when it comes to detecting basic collision and frankly, I can't figure out why.

So my movement for my player is in my Player.cs Class and in this class contains a Update() method that is called in my main game loop in Game1.cs. I have it set up so that when the hitbox of my player 'Rectangle Hitbox;' intersects with a collision tile, it switches a boolean called 'inAir' to false. When this boolean is false, the force of gravity should be zero. The code in my Player.cs's Update method is as follows:

public void Update(GameTime gameTime) {

    //In my constructor for the Player class
    //Position is set equal to a new Vector2(0, 0)
    //Velocity is set equal to Vector2.Zero
    //Acceleration is set equal to a new Vector2(0, 100.0f);

    float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

    Velocity += Acceleration * deltaTime;
    Position += Velocity * deltaTime;

    HitBox = new Rectangle(new Point((int)Position.X, (int)Position.Y), new Point(Width, Height));

    foreach (SpriteTile t in gameMap.CollisionSprites) {
        if (HitBox.Intersects(t.Bounds))
            inAir = false;
        else if (!HitBox.Intersects(t.Bounds))
            inAir = true;
    }

    if (!inAir) {
        Velocity.Y = -Acceleration.Y * deltaTime;
    }

}

I also have some simple text displaying on the screen telling me the value of the boolean 'inAir' which will be shown in the image I provide a bit later.

In my Player.cs class above, a SpriteTile Object only stores 2 things, a Texture2D, and a Position which is predetermined when I load the collisions from my Tiled Collision Layer.

The Bounds Method however simply returns a rectangle showing the bounds of the current tile. It is defined as follows:

public Rectangle Bounds {
    get {
        return new Rectangle(
            (int)Position.X,
            (int)Position.Y,
            Texture.Width,
            Texture.Height);
    }
}

The puzzling part about all this to me is when I added simple debug code to draw these values onto the screen, and test whether the collision's were working, I got this:

enter image description here

According to the bounding boxes I'm drawing, It should be intersecting, therefore stopping the fall. Even if I got the collision response code wrong, the boolean 'inAir' should be set to true. (Yes true because the string is drawn to the screen like so: "Is Intersecting: " + !Player.inAir meaning that if an intersection were to take place, it would draw to the screen true.

If any more information is needed about my code, let me know and I will edit the post and provide it.

If anyone can please help me figure out where I'm going wrong with such a simple idea's implementation, I would be more then grateful! Thanks guys.


Solution

  • The problem is most likely due to an issue with how "inAir" is determined during the update. Under the loop provided, only the last sprite in the CollisionSprites collision could trigger inAir to be false.

    Let's say that gameMap.CollisionSprites contains a 2 sprites. The first sprite you intersect with. The second you don't. On the first pass of the loop HitBox.Intersects(t.Bounds) is true causing inAir to be set to false as you expect. However, the loop does not end and HitBox.Intersects(t.Bounds) is run on the second sprite, which is false, and sets inAir to false.

    There are a couple of ways to correct this. Here is one way:

    inAir = true; // assume you're in the air
    foreach (SpriteTile t in gameMap.CollisionSprites) {
        if (HitBox.Intersects(t.Bounds))
        {
            inAir = false; 
            break; // you've collided, no reason to check any others
        }
    }
    

    Simplified using Linq extension methods...

    inAir = !gameMap.CollisionSprites.Any(t => HitBox.Intersects(t.Bounds));
    

    There are probably other issues you are encountering, but this should solve your collision detection from what I can gather.