2
\$\begingroup\$

I am having trouble with my 2-D wall-sliding. Namely, when the player avatar collides with the wall, it "snaps" to the corner (see screenshot, below) and sticks there. The red, transparent rectangle is my "wall":

enter image description here

My code is as follows:

"FootRectangle" is your standard xna rectangle. Location is a vector2. The player is moving to a target, which could be anywhere on the screen, which is also a vector2.

 if (wall.CollisionRectangle.Intersects(player.FootRectangle))
            {


                // collision above block
                if (player.FootRectangle.Top <= wall.CollisionRectangle.Top)
                {
                    player.Location = new Vector2(player.Location.X, wall.CollisionRectangle.Top - player.FootRectangle.Height / 2);
                }

                // collision below block
                else if (player.FootRectangle.Bottom >= wall.CollisionRectangle.Bottom)
                {
                    //player.FootRectangle.Y = block.CollisionRectangle.Bottom;
                    player.Location = new Vector2(player.Location.X, wall.CollisionRectangle.Bottom + player.FootRectangle.Height / 2);
                }

                // collision left of block
                if (player.FootRectangle.Left <= wall.CollisionRectangle.Left)
                {
                    player.Location = new Vector2(wall.CollisionRectangle.Left - player.FootRectangle.Width / 2, player.Location.Y);
                }

                // collision right of block
                else if (player.FootRectangle.Right >= wall.CollisionRectangle.Right)
                {
                    player.Location = new Vector2(wall.CollisionRectangle.Right + player.FootRectangle.Width / 2, player.Location.Y);
                }

How can I resolve these collisions so that the avatar snaps to a corner and smoothly makes his way to the target destnation?

I attempted to implement a similar solution to that at this post; however, it seemed to stick when the collision occurred.

\$\endgroup\$
1
\$\begingroup\$

Ask yourself these questions: Does your player get stuck on contact? Does your player on contact 'jitters' more into the wall and then gets stuck, or does your player get stuck when moving or jumping fast into the wall.

If it gets smoothly stuck on contact, you need to improve your guard. Are you absolutely sure that the distance between center of your player and the edge of your foot is FootRectangle.Width / 2 ? Try changing your inequality to > and < instead of >= and <=. Or offset your player a little bit more by one pixel or an easy way out, something like (0.51*player.FootRectangle.Width).

If your player jitters into the wall, the other thing that I can think of when looking at your code is maybe you got the signs of the direction of the velocity vector mixed up. Check if it jitters into the wall. If yes, check that going left is indeed defined by a negative sign (and going up with a positive) as you otherwise move the player more into the wall. Change swap then + and - accordingly when before FootRectangle.Width / 2.

(Btw. the way I do resolve stuck collisions is by storing the last known position before the collision and resetting the scene and implement the collision response if this response does not lead to being stuck again (forward check); otherwise set the velocity to the direction of the offset vector.)

\$\endgroup\$
1
\$\begingroup\$

I don't exactly know what you meant by 'stuck'. If it's stuck as in 'can't move in any direction' then the problem probably in the offset distance.

Are FootRectangle.Height, FootRectangle.Width, Location.x, Location.y etc. of type float or int ? If it's an int then dividing it them by 2 (which another int) will only yield an int. Meaning, something like 3/2 will give you 1 instead of 1.5. Thus messing up your offsetting.

You should use float versions of Rectangle and Location to fix this. Or if your Location is of type float then you could cast those variables to float before doing any operations. Like,

player.Location = new Vector2(player.Location.X, (float)wall.CollisionRectangle.Top - (float)player.FootRectangle.Height / 2f);

If it's getting 'stuck', or rather 'hit', when you try to move up then it's behaving as expected because Rectangle.Intersects() is returning true. This means a really small area of your FootRectangleis overlapping the wall.CollisionRectangle. If you want to disregard this minor overlap and proceed as if nothing happened, you could use a modified version of Rectangle.Intersects()or add some small extra offset to the new location.

float smallOffset = .1f;

player.Location = new Vector2(player.Location.X, wall.CollisionRectangle.Top - (player.FootRectangle.Height / 2 + smallOffset) );

Remember this would only work if Location and Rectangle contain float variables.

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

I didn't understand what you mean with 'stuck' but have a look at this post. I shared my simple 2d collision system. Maybe it helps. :)

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

Taking some inspiration from Aj_'s answer, I was able to resolve the collision successfully with the following code:

I found that I needed to test for both: wall.CollisionRectangle.Center.Y - player.FootRectangle.Center.Y) * rectRatio > wall.CollisionRectangle.Center.X - player.FootRectangle.Center.X

and

wall.CollisionRectangle.Center.Y - player.FootRectangle.Center.Y > wall.CollisionRectangle.Center.X - player.FootRectangle.Center.X, etc.

Otherwise, the player's foot rectangle would still clip through the corners of the wall rectangle (varied, depending on which test was omitted.

                // check for collision
                if (wall.CollisionRectangle.Intersects(player.FootRectangle))
                {
                    // set collision offset
                    int collisionOffset = 4;

                    // find height/width ratio for wall rectangle
                    float rectRatio = wall.CollisionRectangle.Width / wall.CollisionRectangle.Height;

                    // resolve collision from top and right
                    if ((wall.CollisionRectangle.Center.Y - player.FootRectangle.Center.Y) * rectRatio > wall.CollisionRectangle.Center.X - player.FootRectangle.Center.X ||
                        wall.CollisionRectangle.Center.Y - player.FootRectangle.Center.Y > wall.CollisionRectangle.Center.X - player.FootRectangle.Center.X)
                    {
                        // right
                        if (player.FootRectangle.Left >= wall.CollisionRectangle.Right - collisionOffset)
                        {
                            player.Location = new Vector2(wall.CollisionRectangle.Right + player.FootRectangle.Width / 2, player.Location.Y);
                        }

                        // top
                        if (player.FootRectangle.Bottom <= wall.CollisionRectangle.Top + collisionOffset)
                        {
                            player.Location = new Vector2(player.Location.X, wall.CollisionRectangle.Top - player.FootRectangle.Height / 2);
                        }
                    }

                    // resolve collision from left and bottom
                    if (wall.CollisionRectangle.Center.X - player.FootRectangle.Center.X > (wall.CollisionRectangle.Center.Y - player.FootRectangle.Center.Y) * rectRatio ||
                        player.CollisionRectangle.Center.X - player.FootRectangle.Center.X > wall.CollisionRectangle.Center.Y - player.FootRectangle.Center.Y)
                    {
                        // bottom
                        if (player.FootRectangle.Top >= wall.CollisionRectangle.Bottom - collisionOffset)
                        {
                            player.Location = new Vector2(player.Location.X, wall.CollisionRectangle.Bottom + player.FootRectangle.Height / 2);
                        }

                        // left
                        if (player.FootRectangle.Right <= wall.CollisionRectangle.Left + collisionOffset)
                        {
                            player.Location = new Vector2(wall.CollisionRectangle.Left - player.FootRectangle.Width / 2, player.Location.Y);
                        }
                    }
                }
\$\endgroup\$
  • \$\begingroup\$ The only type of collision that this code doesn't resolve if when the player's foot rectangle hits directly on the corner. I will update this answer, when I solve this problem. \$\endgroup\$ – Ravenscar Jan 18 '16 at 15:15

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.