🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Allow to slide rectangle avoiding corner collision

Started by
10 comments, last by Asier 5 years, 8 months ago

Hello,

I am detecting each side of collision between two rectangles, and wen I collide them, lets say, with the left side, it also detects collision on the top side when I move to the top, and the downside when I move to the bottom. I think that is because it is detecting also the top/bottom corners.

Left collision

I am detecting each side of collision between two rectangles, and wen I collide them, lets say, with the left side, it also detects collision on the top side when I move to the top, and the downside when I move to the bottom. I think that is because it is detecting also the top/bottom corners.

How I can prevent this corner collision to allow to the player to slide?

Currently, my code is the next one (using xMove (-1 or 1) and yMove (-1 or 1) to know the player movement direction.

 


    boolean isCollisionX = false;
    boolean isCollisionY = false;

    // Check collisions
    for (Rectangle mapCollider : belongsToLevel.tiledMap.mergedColliders) {

        if (playerCollider.intersects(mapCollider)) {

            // Right collision
            if (playerCollider.x - mapCollider.x < 0) {
                isCollisionX = true;
            }
            // Left collision
            if (playerCollider.x - mapCollider.x > 0) {
                isCollisionX = true;
            }

            // Top collision
            if (playerCollider.y - mapCollider.y > 0) {
                isCollisionY = true;
            }
            // Bottom collision
            if (playerCollider.y - mapCollider.y < 0) {
                isCollisionY = true;
            }

            break;
        }
    }

    if (!isCollisionX) {
        pos.x += xMove;
    }
    if (!isCollisionY) {
        pos.y += yMove;
    }

Thank you in advance.

Advertisement

Regarding this code:


// Right collision
if (playerCollider.x - mapCollider.x < 0) {
    isCollisionX = true;
}
// Left collision
if (playerCollider.x - mapCollider.x > 0) {
    isCollisionX = true;
}

// Top collision
if (playerCollider.y - mapCollider.y > 0) {
    isCollisionY = true;
}
// Bottom collision
if (playerCollider.y - mapCollider.y < 0) {
    isCollisionY = true;
}

What are x and y? Do they represent the centers of the rectangles? Or perhaps a corner? How are the rectangles represented? (E.g. min/max, min/size, center/extent, etc.)

Also, you mentioned that the move deltas are +/-1. Is the movement time-based? Or are the deltas fixed, e.g. one pixel per move or something like that?

Hello Zakwayda,

The x and y corner of the colliders represent like so, The x and y represent the top left corners of the rectangles, and the top right corners, the max width and height.

image.png.d55f3503070b27d61f8346b7931bfe10.png

 

Then, about the movement of the player, I am reading the keyboard like this, and moving 1 pixel (int) to the desired direction. (The method move(xMove, yMove) has the collission code that I post previously.


@Override
public void update() {
    super.update();

    int xMove = 0;
    int yMove = 0;

    // Read keyboard events
    if (keyboard.up) {
        yMove--;
    }
    else if (keyboard.down) {
        yMove++;
    }
    else if (keyboard.left) {
        xMove--;
    }
    else if (keyboard.right) {
        xMove++;
    }
 
    if (xMove != 0 || yMove != 0) {
        move(xMove, yMove);
    }
}

Thank you so much for your response.

Quote

The x and y corner of the colliders represent like so, The x and y represent the top left corners of the rectangles, and the top right corners, the max width and height.

Your picture is clear, but when you say 'the top right corners', do you actually mean bottom-right? It looks to me like top-left is (x, y), and bottom-right is (x+width, y+height).

I don't know what info your rectangle class provides, but the (x, y, width, height) representation can be kind of awkward for things like collision detection. A min/max representation can be easier to work with, where min = (x, y), and max = (x+width, y+height). All that really does of course is give a 'name' to (x+width, y+height), but even that can simplify things considerably and eliminate some possible programming errors.

I'm wondering about this code:


// Right collision
if (playerCollider.x - mapCollider.x < 0) {
    isCollisionX = true;
}
// Left collision
if (playerCollider.x - mapCollider.x > 0) {
    isCollisionX = true;
}

And the corresponding y-axis code. Maybe I'm overlooking something, but it looks to me like isCollisionX will be set to true whenever playerCollider.x != mapCollider.x. I kind of doubt that's what you intend. Can you explain what this code is intended to do?

Based on the code you posted, I'm guessing that isCollisionX and isCollisionY are both being set to true for every collision. Is that what you're seeing in practice?

Hello again Zakwayda,

Yeah... I am sorry, (x=0, y=0) is top left corner and (x=x + width, y=y + height) the bottom right corner as you said.

About the Rectagle class, I am using the java.awt.Rectangle Java class. As you say, I can use the x, y, width and heigth to calculate the right side with x + width and the bottom side y + width.

About the code, as you said, if I collide for example with the right side, I won't be able to move to the left (and vice versa), due to isCollisionX=true for the both cases. So the right/left movements are not going to be able, and that is incorrect for me. Maybe I need to create a boolean for each side collision?

For the direction of the player,I also have the followind code if this helps, where I can detect the direction of the player using the xMove and the yMove, which can be 1 or -1 pixel.


if (yMove < 0) {
	direction = Direction.NORTH;
}
else if (yMove > 0) {
	direction = Direction.SOUTH;
}
if (xMove < 0) {
	direction = Direction.WEST;
}
else if (xMove > 0) {
	direction = Direction.EAST;
}

I am also thinking about what you said of the max, min of the rectagles, for the collision of the right side, and the bottom side, I think that I need to use x + width and y + height to compare this collision.

I tried with (playerCollider.x + playerCollider.width) > mapCollider.x (for right collision) and playerCollider.x < (mapCollider.x + mapCollider.width) for the left, but I am not able to make is work. I think that I making a mess :(

Thank you so much.

Since you're moving in integer increments of +/-1, I think there might be an easier way to do what you're wanting to do. (If you ever move in increments larger that +/-1, you might still be able to use this method by stepping through the move in increments of +/-1.)

Please note that this is speculative. I haven't tested it myself.

Although your last post includes some code that seems to suggest only horizontal and vertical motion is allowed (due to the mention of north/south/west/east), based on earlier comments I'm assuming diagonal motion is also possible.

So you have x and y deltas, each of which is 0 or +/-1. The first step is to perform an intersection test at the position (x+xDelta, y+yDelta). If there's no intersection, you can move the player to this position and you're done.

If there is an intersection, look at the x and y deltas. If either is 0, the move is horizontal or vertical. In this case no sliding can occur, so the move is blocked and you're done (just leave the player where it is).

If the x and y deltas are both +/-1, the move is diagonal, and sliding may occur.

In this case, perform two more intersection tests, one at (x+xDelta, y) and one at (x, y+yDelta). There are four possibilities:

- Both intersection tests return true. In this case the player is in a corner and can't move.

- One intersection test returns true and the other returns false (this covers two of the four cases). In this case, move the player to the position for which the test returned false.

- Both intersection tests return false. In this case, use whatever method you like (e.g. random selection) to select one of the two directions to move.

Again, this is speculative and untested. But if it's correct, it seems like it would be a fairly straightforward solution.

[Edit: Minor clarification.]

Only check the x-collision if moving right-left and only check the y-collision if moving up-down.

 

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

Hi guys,

I have been testing and I have understand much better the logic of the collision. I also separated each side with a boolean and I check the x axis collision and then, the y axis collision.

My only problem now is that when the right side or the top side collides, I am checking with >= and this causes true in all cases for right, not allowing to check the next else if for the other side :(


if (collider.intersects(mapCollider)) {

	if (direction == Direction.WEST || direction == Direction.EAST) {
		// Right collision
		if (collider.x + collider.width >= mapCollider.x) {
			isCollisionRight = true;
		}
		// Left collision
		else if (collider.x <= mapCollider.x + mapCollider.width) {
			isCollisionLeft = true;
		}                   
	}

	if (direction == Direction.NORTH || direction == Direction.SOUTH) {
		// Top collision
		if (collider.y + collider.height >= mapCollider.y) {
			isCollisionTop = true;
		}
		// Bottom collision
		else if (collider.y <= mapCollider.y + mapCollider.height) {
			isCollisionBottom = true;
		}
    }
}

I tried to limit the sides check, but I return to my first problem, The player can't slide.


if (collider.intersects(mapCollider)) {

	if (direction == Direction.WEST || direction == Direction.EAST) {
		// Right collision
		if (collider.x + collider.width >= mapCollider.x && collider.x <= mapCollider.x) {
			System.out.println("right");
			isCollisionRight = true;
		}
		// Left collision
		else if (collider.x <= mapCollider.x + mapCollider.width && collider.x + collider.width >= mapCollider.x) {
			System.out.println("left");
			isCollisionLeft = true;
		}
	}

	if (direction == Direction.NORTH || direction == Direction.SOUTH) {
		// Top collision
		if (collider.y <= mapCollider.y + mapCollider.height && collider.y >= mapCollider.y) {
			System.out.println("top");
			isCollisionTop = true;
		}
		// Bottom collision
		else if (collider.y <= mapCollider.y + mapCollider.height && collider.y + collider.height >= mapCollider.y) {
			System.out.println("bottom");
			isCollisionBottom = true;
		}
	}
	break;
}

Thank you so much.

Hi guys! In the end I ended up with the next code, using the center of the rectangles. Do you see this correct? or is it inefficient?


	if (playerCollider.intersects(mapCollider)) {
		
      	int dx = (collider.x + collider.width / 2) - (obstacle.x + obstacle.width / 2);
    	int dy = (collider.y + collider.height / 2) - (obstacle.y + obstacle.height / 2);
    	int width = (collider.width + obstacle.width) / 2;
    	int height = (collider.height + obstacle.height) / 2;
    	int crossWidth = width * dy;
    	int crossHeight= height * dx;

	    if (crossWidth > crossHeight) {
	    	if (crossWidth > -crossHeight) {
	    		System.out.println("Top!");
	    		isCollisionTop = true;
	    	}
	    	else {
	    		System.out.println("Right!");
	    		isCollisionRight = true;
	    	}
	    } else {
	    	if (crossWidth > -crossHeight) {
	    		System.out.println("Left!");
	    		isCollisionLeft = true;
	    	}
	    	else {
	    		System.out.println("Bottom!");
	    		isCollisionBottom = true;
	    	}
	    }
    }

Thank youu! :)

Does that code work? Or is the behavior still incorrect?

This topic is closed to new replies.

Advertisement