1
\$\begingroup\$

I'm trying to make a character collide with some tiled terrain.

I have an issue with the collision resolution, and after days of shooting in the dark ant not finding any relevant help online, I'm no closer to working collision resolution.

I can detect if the player is colliding with the tiles, but I can't figure out how to properly resolve the collision.

I have is so gravity is only applied when there is no collisions in the downward direction, and I have set it so that the proper velocity resolution, so that if I collide with the ground, I don't go through it, but I still go a few pixels into the tile if going at high velocities, and for some reason, if I'm colliding with a surface whose normal is along the x axis (floors and ceilings) It decides that I'm also colliding on the left and right sides as well, and I don't collide with vertical walls at all.

I should note that I'm using MonoGame 3.4.

Physics update code for character

public void Update()
    {
        Position += Velocity * TimeHandler.DeltaTime;

        //TODO: add tile collision based on the entity.world property
        var tiles = entity.World.Tiles;

        int minTileX = (int)Math.Floor (Bounds.MinX);
        int maxTileX = (int)Math.Ceiling (Bounds.MaxX);
        int minTileY = (int)Math.Floor (Bounds.MinY) - 1;
        int maxTileY = (int)Math.Ceiling (Bounds.MaxY) - 1;

        bool collidedNegY = false;
        for (int x = minTileX; x <= maxTileX; x++)
        {
            for (int y = minTileY; y <= maxTileY; y++)
            {
                bool kKeyDown = Keyboard.GetState ().IsKeyDown (Keys.K);
                if (kKeyDown)
                    tiles.SetID (1, x, y);
                bool jKeyDown = Keyboard.GetState ().IsKeyDown (Keys.J);
                if (jKeyDown)
                    tiles.SetID (0, x, y);

                AABB tileAABB = new AABB (new Vector2(x, y), new Vector2(1f, 1f), new Vector2(0f, 1f));
                //CollisionInfo info;
                if (tileAABB.Check (Bounds))
                {
                    if (tiles.GetID (x, y) == 0)
                        continue;

                    Vector2 collisionDirection = tileAABB.Position - Bounds.Position;
                    bool xIsRight = collisionDirection.X > 0;
                    bool yIsUp = collisionDirection.Y > 0;
                    bool differentX = xIsRight == Velocity.X > 0;
                    bool differentY = yIsUp == Velocity.Y > 0;

                    if (differentX)
                    {
                        Velocity.X *= 0f;
                        if (Bounds.MaxX > tileAABB.MaxX)
                        {

                        }
                        if (Bounds.MinX < tileAABB.MinX)
                        {

                        }
                    }
                    if (differentY)
                    {
                        Velocity.Y *= 0f;
                        if (Bounds.MaxY > tileAABB.MaxY)
                        {
                            collidedNegY = true;
                        }
                        if (Bounds.MinY < tileAABB.MinY)
                        {

                        }
                    }
                }
            }
        }
        if(!collidedNegY)       
        {
            //apply gravity
            Velocity.Y -= entity.World.Gravity * TimeHandler.DeltaTime;
        }

        Bounds.Position = Position;
    }

AABB class:

public struct AABB
{
    private Vector2 min;
    private Vector2 max;
    private float y;
    private float x;
    private float width;
    private float height;
    private Vector2 offset;

    //position
    public Vector2 Position
    {
        get
        {
            return new Vector2 (x, y);
        }
        set
        {
            UpdateBounds (value.X, value.Y);
        }
    }

    public Vector2 Size
    {
        get
        {
            return new Vector2 (width, height);
        }
        set
        {
            UpdateBounds (x, y, value.X, value.Y);
        }
    }

    public float MinX
    {
        get
        {
            return min.X;
        }
    }
    public float MinY
    {
        get
        {
            return min.Y;
        }
    }
    public float MaxX
    {
        get
        {
            return max.X;
        }
    }
    public float MaxY
    {
        get
        {
            return max.Y;
        }
    }

    public float Width
    {
        get
        {
            return width;
        }
        set
        {
            UpdateBounds (min.X, min.Y, value, height);
        }
    }
    public float Height
    {
        get
        {
            return height;
        }
        set
        {
            UpdateBounds (min.X, min.Y, width, value);
        }
    }

    //update
    private void UpdateBounds(float x, float y)
    {
        this.x = x;
        this.y = y;
        min = new Vector2 (x + offset.X, y + offset.Y);
        max = new Vector2 (min.X + width, min.Y + height);
    }
    private void UpdateBounds(float x, float y, float width, float height)
    {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        min = new Vector2 (x + offset.X, y + offset.Y);
        max = new Vector2 (min.X + width, min.Y + height);
    }

    //constructors
    public AABB(Vector2 pos, Vector2 size, Vector2 offset)
    {
        this.x = pos.X;
        this.y = pos.Y;
        this.width = size.X;
        this.height = size.Y;
        this.offset = offset;
        this.min = new Vector2(pos.X + offset.X, pos.Y + offset.Y);
        this.max = new Vector2 (min.X + size.X, min.Y + size.Y);
    }

    //checking
    public bool Check(AABB b)
    {
        if (this.max.X < b.min.X || this.min.X > b.max.X)
            return false;
        if (this.max.Y < b.min.Y || this.min.Y > b.max.Y)
            return false;

        return true;
    }

    public static bool Check(AABB a, AABB b)
    {
        return a.Check (b);
    }
}
\$\endgroup\$
1
\$\begingroup\$

Collision resolution is no easy beast. You need to take a step back and realize that you're going to need to simulate some simple physics. Game physics don't have to be super accurate and you can tweak gravity and resultant forces however you want but you need a solid framework to build off of. I recommend that when handling collision resolution you have a Physics management system that's separate from your collision detection system.

How I usually do this is with something along the lines of

//Update game logic
//Detect collisions (and store collision data)
//Go through collisions, apply reactionary forces
//Apply forces to all objects (gravity is a force)
//Go back and re-detect collisions (if I want to be super accurate)

You're going to need to simulate some basic kinetics. There should be a way for you to apply forces to your objects and derive an appropriate acceleration from that. From there you can look into determining how objects will interact with each other after a collision.

Chris Hecker has some AMAZING resources on this. They apply to 2D and 3D. I've built an okay framework for 3D physics around his resources and it's turned out quite well so far.

http://chrishecker.com/Rigid_body_dynamics

http://chrishecker.com/images/e/e7/Gdmphys3.pdf

You might be thinking "Isn't rigidbody dynamics a bit much?" Yes and no. You don't have to try to implement everything in these papers, just focus on what you need. You don't need to rotate anything yet and you don't need to think about 3D.

Collision resolution isn't easy. You may need to come back and ask some more specific questions later :)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.