1
\$\begingroup\$

I have recently been experimenting with Monogame and XNA (moving from Gamemaker) and decided to work on a simple collision system. Problem is shown in the GIF. Preview of the bug

Main Collision

Each entity (Quick name for anything with properties) has some parameters. Those are:

    public Vector2 Position;
    public Vector2 Origin;
    public Vector2 Velocity;
    public Vector2 Speed;
    public Color Color = Color.White;
    public Rectangle Rectangle
    {
        get
        {
            return new Rectangle((int)Position.X, (int)Position.Y, _texture.Width, _texture.Height);
        }

    }

I used blocks just to check how collision works on multiple objects next to each other. This is not about optimisation right now. As you can see, there is an invisible wall coming to the right of each vertical block. I can add multiples just to demonstrate:

enter image description here

Now let's get to the collision code. I wanted to do something similar like in GMS2 and check if the box of sprite is going to overlap any other entity in the game at set postition. Since I need to know in the future, which side will be touched, I wrote my code in Entity class like this:

    protected bool IsTouchingLeft(Vector2 pos, Entity entity)
    {
        return pos.X + _texture.Width >= entity.Rectangle.Left &&
               pos.X <= entity.Rectangle.Left &&
               pos.Y + _texture.Height >= entity.Rectangle.Top &&
               pos.Y <= entity.Rectangle.Bottom;
    }
    protected bool IsTouchingRight(Vector2 pos, Entity entity)
    {
        return pos.X >= entity.Rectangle.Right &&
               pos.X + _texture.Width >= entity.Rectangle.Right &&
               pos.Y + _texture.Height >= entity.Rectangle.Top &&
               pos.Y <= entity.Rectangle.Bottom;
    }
    protected bool IsTouchingTop(Vector2 pos, Entity entity)
    {
        return pos.X <= entity.Rectangle.Right &&
               pos.X + _texture.Width >= entity.Rectangle.Left &&
               pos.Y + _texture.Height >= entity.Rectangle.Top &&
               pos.Y <= entity.Rectangle.Top;
    }
    protected bool IsTouchingBottom(Vector2 pos, Entity entity)
    {
        return pos.X <= entity.Rectangle.Right &&
               pos.X + _texture.Width >= entity.Rectangle.Left &&
               pos.Y <= entity.Rectangle.Bottom &&
               pos.Y + _texture.Height >= entity.Rectangle.Bottom;
    }

For these functions I use two variables, the position at which collision is going to happen and Entity that it should collide with.

And to make everything simple when I just need to know, if it's going to collide at all, I added a PlaceMeeting function.

    protected bool PlaceMeeting(Vector2 pos, Entity entity)
    {
        return IsTouchingLeft(pos, entity) ||
               IsTouchingRight(pos, entity) ||
               IsTouchingTop(pos, entity) ||
               IsTouchingBottom(pos, entity);
    }

It just checks if there wil be any collision at a set point with a set Entity. ( if you've used GMS, you know what I want to do )

Now Let's go to the movement script. Player is a child of Entity class and has few new variables.

    private Vector2 move;
    private float grav = 0.3f;
    private Int32 input_left;
    private Int32 input_right;
    private bool input_up;

Input is taken like this:

        input_left = Convert.ToInt32(Keyboard.GetState().IsKeyDown(Keys.A));
        input_right = Convert.ToInt32(Keyboard.GetState().IsKeyDown(Keys.D));
        input_up = Keyboard.GetState().IsKeyDown(Keys.Space);

Horizontal input is set in such way because of simpler movement. Here is Move() Function.

    private void Move()
    {
        move.X = (input_right - input_left);
        Velocity.X = move.X * speed.X;
        Velocity.Y += grav;
        if (input_up)
            Velocity.Y = speed.Y;
    }

And the the code Player Collision code:

        foreach (var entity in entities) //Looks at all entities created
        {
            if (entity == this) //If it is player, don't check collision
                continue;

            //Horizontal Collision               
            if (PlaceMeeting(new Vector2(Position.X + Velocity.X, Position.Y), entity))
            {
                while (!PlaceMeeting(new Vector2(Position.X + Math.Sign(Velocity.X), Position.Y), entity))
                {
                    Position.X += Math.Sign(Velocity.X);
                }
                Velocity.X = 0;
            }
            //Vertical Collision
            if (PlaceMeeting(new Vector2(Position.X, Position.Y + Velocity.Y), entity)) // checking if it's going to collide in 1 step
            {
                while (!PlaceMeeting(new Vector2(Position.X, Position.Y + Math.Sign(Velocity.Y)), entity))// checking if it's not going to collide in 1 pixel
                {
                    Position.Y += Math.Sign(Velocity.Y); // incrementing pixel By pixel until it's going to collide in 1 pixel
                }
                Velocity.Y = 0; //stoping the player
            }
        }

And then I Increment the position with Velocity.

Position += Velocity;

I tried different collision methods and this is how they work out and look like:

Other Collision One

In the Player Class I used this collision code instead:

            if (this.Velocity.X > 0 && IsTouchingLeft(Position + Velocity, entity))
                this.Velocity.X = 0;
            if (this.Velocity.X < 0 && IsTouchingRight(Position + Velocity, entity))
                this.Velocity.X = 0;
            //Vertical Collision
            if (this.Velocity.Y > 0 && IsTouchingTop(Position + Velocity, entity))
                this.Velocity.Y = 0;
            if (this.Velocity.Y < 0 && IsTouchingBottom(Position + Velocity, entity))
                this.Velocity.Y = 0; 

I basically couldn't move Left when in the right of vertical blocks.

Other Collision 2

I also tried using this strange code with Rectangle.Intersects()

            //Horizontal Collision
            var xRect = new Rectangle((int)Position.X+ (int)Velocity.X, (int)Position.Y, _texture.Width, _texture.Height);
            if (xRect.Intersects(entity.Rectangle))
            {
                xRect = new Rectangle((int)Position.X + Math.Sign(Velocity.X), (int)Position.Y, _texture.Width, _texture.Height);
                while (!xRect.Intersects(entity.Rectangle))
                {
                    Position.X += Math.Sign(Velocity.X);
                }
                this.Velocity.X = 0;


            }
            //Vertical Collision
            var yRect = new Rectangle((int)Position.X, (int)Position.Y + (int)Velocity.Y, _texture.Width, _texture.Height);
            if (yRect.Intersects(entity.Rectangle))
            {
                yRect = new Rectangle((int)Position.X, (int)Position.Y + Math.Sign(Velocity.Y), _texture.Width, _texture.Height);
                while (!yRect.Intersects(entity.Rectangle))
                {
                    Position.Y += Math.Sign(Velocity.Y);
                }
                this.Velocity.Y = 0;

            }

This made my program to unresponsive - why? What can I do to prevent it from becoming unresponsive?

I will post the full code of each class: Game1.cs Entity.cs Player.cs Block.cs

\$\endgroup\$
0
\$\begingroup\$

You should check if your IsTouchingLeftand isTouchingRight are working as intended.

I've noticed that in your IsTouchingRight, You're doing:

pos.X + _texture.Width >= entity.Rectangle.Right

However, that's not yet the opposite of this code in your IsTouchingLeft

pos.X + _texture.Width >= entity.Rectangle.Left

Because it's still calculating if it's x-position is greater (more to the right) than the rectangle's x-position, but you're almost there.

You should change the >= into <= or < of the line in IsTouchingRight, and try testing around in that section:

pos.X + _texture.Width <= entity.Rectangle.Right

A little tip: I would've drawn the results of these booleans in the game (using Drawstring()) so I have up-to-date information whether the values are intentional at that moment. that way I know the values without having to use breakpoints. Of course you should only leave them there for testing purposes.

\$\endgroup\$
  • \$\begingroup\$ sidenote: I think that when you're checking the left collision, you also don't need to take _texture.Width with you, to get the left side of the texture, but that's a part of the "testing around in that section" :) \$\endgroup\$ – Steven Jul 3 at 6:23

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.