I have spent hours looking for the solution to this: I am developing a little top-down game with libgdx (maybe it matters what engine i am using). Now i have to implement the collision detection between my character (circle) and the wall (rectangle). I want the character to slide along the wall on collision, if sliding is possible.
Let me explain:
If i am moving 45 degrees right up i can collide with the down, the
left or the corner of a wall.
If i collide with the left i want to stop x-movement and move only up. If i leave the wall then i want to go on moving right up. The same
with the down side (stop y-movement)
If i collide with the Corner i want to stop movement (sliding not possible).
What i am doing actually is to check if the left line of the rectangle intersects my circle. Then i check intersection between the left line of wall and my circle and the bottom line of wall and my circle. Depending on which intersection occuret i set back x/y possition of my circle and set x/y Speed to 0. The Problem is, that most times not a collision bt an overlap occures. So the bottom check returns true, even if in reality the circle would only collide with the right. In this case both intersection test would return true and i would reset both speeds like on the Corner collision.
How can i solve this Problem? Is ther a better way to detect collision and collision side or corner?
I don't Need the exact Point of collision just the side of the rectangle.
Edit:
I have to say, that the rects aren't rotated just parallel to the x-axis.
You can find an explanation for circle/rectangle collision below, but please note that this type of collision might not be necessary for your needs. If, for example, you had a rectangle bounding box for your character the algorithm would be simpler and faster. Even if you are using a circle, it is probable that there is a simpler approach that is good enough for your purposes.
I though about writing the code for this, but it would take too long so here is only an explanation:
Here is a example movement of your character circle, with its last (previous) and current positions. Wall rectangle is displayed above it.
Here is that same movement, dotted lines represent the area the circle sweeps in this move. The sweep area is capsule shaped.
It would be difficult to calculate the collision of these two object, so we need to do this differently. If you look at the capsule on the previous image, you will see that it is simply the movement line extended in every direction by the radius of the circle. We can move that "extension" from the movement line to the wall rectangle. This way we get a rounded rectangle like on the image below.
The movement line will collide with this extended (rounded) rectangle if and only if the capsule collides with the wall rectangle, so they are somehow equivalent and interchangeable.
Since this collision calculation is still non-trivial and relatively expensive, you can first do a fast collision check between the extended wall rectangle (non-rounded this time) and the bounding rectangle of the movement line. You can see these rectangles on the image below - they are both dotted. This is a fast and easy calculation, and while you play the game there will probably NOT be an overlap with a specific wall rectangle >99% of the time and collision calculation will stop here.
If however there is an overlap, there is probably a collision of the character circle with wall rectangle, but it is not certain as will be demonstrated later.
Now you need to calculate the intersection between the movement line itself (not its bounding box) and the extended wall rectangle. You can probably find an algorithm how to do this online, search for line/rectangle intersection, or line/aabb intersection (aabb = Axis Aligned Bounding Box). The rectangle is axis-aligned and this makes the calculation simpler. The algorithm can give you intersection point or points since it is possible that there are two - in this case you choose the closest one to the starting point of the line. Below is an example of this intersection/collision.
When you get an intersection point, it should be easy to calculate on which part of the extended rectangle this intersection is located. You can see these parts on the image above, separated by red lines and marked with one or two letters (l - left, r - right, b - bottom, t - top, tl - top and left etc).
If the intersection is on parts l, r, b or t (the single letter ones, in the middle) then you are done. There is definitely a collision between character circle and wall rectangle, and you know on which side. In the example above, it is on the bottom side. You should probably use 4 variables called something like isLeftCollision, isRightCollision, isBottomCollsion and isTopCollision. In this case you would set isBottomCollision to true, while the other 3 would remain at false.
However, if the intersection is on the corner, on the two-letter sections, additional calculations are needed to determine if there is an actual collision between character circle and wall rectangle. Image below shows 3 such intersections on the corners, but there is an actual circle-rectangle collision on only 2 of them.
To determine if there is a collision, you need to find an intersection between the movement line and the circle centered in the closest corner of the original non-extended wall rectangle. The radius of this circle is equal to the radius of character circle. Again, you can google for line/circle intersection algorithm (maybe even libgdx has one), it isn't complex and shouldn't be hard to find.
There is no line/circle intersection (and no circle/rectangle collision) on bl part, and there are intersections/collisions on br and tr parts.
In the br case you set both isRightCollision, isBottomCollsion to true and in the tr case you set both isRightCollision and isTopCollision to true.
There is also one edge case you need to look out for, and you can see it on the image below.
This can happen if the movement of previous step ends in the corner of the the extended rectangle, but outside the radius of the inner rectangle corner (there was no collision).
To determine if this is the case, simply check if movement staring point is inside the extended rectangle.
If it is, after the initial rectangle overlap test (between extended wall rectangle and bounding rectangle of movement line), you should skip line/rectangle intersection test (because in this case there might not be any intersection AND still be a circle/rectangle collision), and also simply based on movement stating point determine which corner you are in, and then only check for line/circle intersection with that corner's circle. If there is intersection, there is a character circle/wall rectangle collision, otherwise not.
After all of this, the collision code should be simple:
// x, y - character coordinates
// r - character circle radius
// speedX, speedY - character speed
// intersectionX, intersectionY - intersection coordinates
// left, right, bottom, top - wall rect positions
// I strongly recomment using a const "EPSILON" value
// set it to something like 1e-5 or 1e-4
// floats can be tricky and you could find yourself on the inside of the wall
// or something similar if you don't use it :)
if (isLeftCollision) {
x = intersectionX - EPSILON;
if (speedX > 0) {
speedX = 0;
}
} else if (isRightCollision) {
x = intersectionX + EPSILON;
if (speedX < 0) {
speedX = 0;
}
}
if (isBottomCollision) {
y = intersectionY - EPSILON;
if (speedY > 0) {
speedY = 0;
}
} else if (isTopCollision) {
y = intersectionY + EPSILON;
if (speedY < 0) {
speedY = 0;
}
}
[Update]
Here is a simple and I believe efficient implementation of segment-aabb intersection that should be good enough for your purposes. It is a slightly modified Cohen-Sutherland algorithm. Also you can check out the second part of this answer.
public final class SegmentAabbIntersector {
private static final int INSIDE = 0x0000;
private static final int LEFT = 0x0001;
private static final int RIGHT = 0x0010;
private static final int BOTTOM = 0x0100;
private static final int TOP = 0x1000;
// Cohen–Sutherland clipping algorithm (adjusted for our needs)
public static boolean cohenSutherlandIntersection(float x1, float y1, float x2, float y2, Rectangle r, Vector2 intersection) {
int regionCode1 = calculateRegionCode(x1, y1, r);
int regionCode2 = calculateRegionCode(x2, y2, r);
float xMin = r.x;
float xMax = r.x + r.width;
float yMin = r.y;
float yMax = r.y + r.height;
while (true) {
if (regionCode1 == INSIDE) {
intersection.x = x1;
intersection.y = y1;
return true;
} else if ((regionCode1 & regionCode2) != 0) {
return false;
} else {
float x = 0.0f;
float y = 0.0f;
if ((regionCode1 & TOP) != 0) {
x = x1 + (x2 - x1) / (y2 - y1) * (yMax - y1);
y = yMax;
} else if ((regionCode1 & BOTTOM) != 0) {
x = x1 + (x2 - x1) / (y2 - y1) * (yMin - y1);
y = yMin;
} else if ((regionCode1 & RIGHT) != 0) {
y = y1 + (y2 - y1) / (x2 - x1) * (xMax - x1);
x = xMax;
} else if ((regionCode1 & LEFT) != 0) {
y = y1 + (y2 - y1) / (x2 - x1) * (xMin - x1);
x = xMin;
}
x1 = x;
y1 = y;
regionCode1 = calculateRegionCode(x1, y1, r);
}
}
}
private static int calculateRegionCode(double x, double y, Rectangle r) {
int code = INSIDE;
if (x < r.x) {
code |= LEFT;
} else if (x > r.x + r.width) {
code |= RIGHT;
}
if (y < r.y) {
code |= BOTTOM;
} else if (y > r.y + r.height) {
code |= TOP;
}
return code;
}
}
Here is some code example usage:
public final class Program {
public static void main(String[] args) {
float radius = 5.0f;
float x1 = -10.0f;
float y1 = -10.0f;
float x2 = 31.0f;
float y2 = 13.0f;
Rectangle r = new Rectangle(3.0f, 3.0f, 20.0f, 10.0f);
Rectangle expandedR = new Rectangle(r.x - radius, r.y - radius, r.width + 2.0f * radius, r.height + 2.0f * radius);
Vector2 intersection = new Vector2();
boolean isIntersection = SegmentAabbIntersector.cohenSutherlandIntersection(x1, y1, x2, y2, expandedR, intersection);
if (isIntersection) {
boolean isLeft = intersection.x < r.x;
boolean isRight = intersection.x > r.x + r.width;
boolean isBottom = intersection.y < r.y;
boolean isTop = intersection.y > r.y + r.height;
String message = String.format("Intersection point: %s; isLeft: %b; isRight: %b; isBottom: %b, isTop: %b",
intersection, isLeft, isRight, isBottom, isTop);
System.out.println(message);
}
long startTime = System.nanoTime();
int numCalls = 10000000;
for (int i = 0; i < numCalls; i++) {
SegmentAabbIntersector.cohenSutherlandIntersection(x1, y1, x2, y2, expandedR, intersection);
}
long endTime = System.nanoTime();
double durationMs = (endTime - startTime) / 1e6;
System.out.println(String.format("Duration of %d calls: %f ms", numCalls, durationMs));
}
}
This is the result I get from executing this:
Intersection point: [4.26087:-2.0]; isLeft: false; isRight: false; isBottom: true, isTop: false
Duration of 10000000 calls: 279,932343 ms
Please note that this is desktop performance, on an i5-2400 CPU. It will probably be much slower on Android devices, but I believe still more than sufficient.
I only tested this superficially, so if you find any errors, let me know.
If you use this algorithm, I believe you don't need special handling for that case where starting point is in the corner of the extended wall rectangle, since in this case you will get the intersection point at line start, and the collision detection procedure will continue to the next step (line-circle collision).
I suppose you determine the collision by calculating the distance of the circles center with the lines.
We can simplify the case and tell that the circle colliding with the corner if both distances are equal and smaller than the radius. The equality should have a tolerance of course.
More - may be not necessary- realistic approach would be to consider x,y speed and factor it in the equality check.
I've noticed some very strange behaviour with a simple piece of code, I've stripped it down to remove any variables, time-step, acceleration, etc.
I have a shape on the screen, pressing the left side of the screen thrusts the shape right, pressing the right side thrusts left. For some reason the shape favours moving left (speed < 0). It seems to be that it decelerates properly when moving right but when moving left it finishes decelerating then continues for maybe 0.3 seconds before stopping.
float speed;
int x;
Update method for the shape:
speed *= 0.9f;
if (Math.abs(speed) < 0.1f)
speed = 0;
if (Gdx.input.isTouched()) {
if (Gdx.input.getX() < Gdx.graphics.getWidth() / 2) {
speed = 10;
} else {
speed = -10;
}
}
x += speed;
x should be defined as float. I used int because the viewport is set to the screen size but finally realised it needs to have the precision.
I'm trying to make a platform game. I have the collision code (almost) but there seems to be a bug. I try this code:
for (int i = 0; i < world.ground.size(); i++) {
if (!world.ground.get(i).intersects((int) x, (int) y, player_width, player_height + (int) dy)) {
y += dy;
if (dy < 4) {
dy += 0.1;
}
} else {
dy = 0;
jumped = false;
}
}
But sometimes my character's foot goes through the ground by 2 or 3 pixels. Is there a better way to do this? Please help, thanks.
It seems like you are using a posteriori (discrete) collision detection. This causes your object penetrate through a little every time becuse it activates only when touched or penetrated. You may think to convert this to a priori (continuous) collision detection. This way, it never penetrates the ground because it checks before the collision then adjusts speed or position in order to avoid penetration.
If you dont want to fiddle with this kind, you can just add a correction function that acts before painting.
void foo()
{
//check if below ground
//if yes, displecement upwards until it is above enough
//now you can paint
//return
}
I see you implemented this:
what_you_need_to_make_it_above_ground=(ground_y-feet_y);
//im not sure if dy is ground level so i added second boolean compare
if ((dy < 4)||(ground_y>feet_y)) {
dy += 0.1; // this isnt enough. should be equal to:
dy +=what_you_need_to_make_it_above_ground;
dy +=if_there_are_other_parameters_think_of_them_too;
}
I have given a diagram of my current small problem that I need help with. My main purpose is to keep the point from going outside the circle. Nothing else.
The center of the circle is positioned at (x, y).
I only solved a little bit of the problem, and that is the collision detection part of my problem, as given below:
public void bound(Point p, Circle c){
double distance = Math.hypot(p.x - c.x, p.y - c.y);
if (distance >= c.radius){
//Clueless from here on out.
}
}
The part where I left a comment is the spot I couldn't figure anything out. I did tried to set the point's velocityX and velocityY to 0, but I realized the point will just stay put whenever it touches the circle.
So, I'm sort of stuck.
I have resolved this issue.
public void reflect(Hole h){
//R = -2*(V dot N)*N + V
//N is normalized.
double nx = (this.position[0]+this.diameter/2) - (h.x+16);
double ny = (this.position[1]+this.diameter/2) - (h.y+16);
double nd = Math.hypot(nx, ny);
if (nd == 0)
nd = 1;
nx /= nd;
ny /= nd;
double dotProduct = this.speed[0]*nx+this.speed[1]*ny;
this.speed[0] += (float)(-2*dotProduct*nx);
this.speed[1] += (float)(-2*dotProduct*ny);
}
public void reflectResponse() {
for (int i = 0; i <= 1; i++) {
position[i] -= speed[i];
speed[i] *= 0.992f;
}
}
I tried Oli Charlesworth's method from the comments, but it made things more... "complicated" than I expected. Someone else mentioned I used a completely 100%, vector-based algorithm, since I'm relying a lot on vector-based movements.
TIPS TO THOSE WHO DO READ THIS:
If you're working on object movements and collisions with vectors, seek vector-based algorithms.
If you're working with angles (either degrees or radians), use Oli Charlesworth's method.
So i'm just trying to make a ball bounce around the screen which should slow down due to gravity and reflect (bounce) from the wall like a normal ball would.
Can someone give some basics and VERY simple implementation of this?
Other examples seem a bit "overdone" and seem to go beyond what I want to do.
I've tried this:
public void updateLogic() {
if (x < -1) {
xPos += (-x * gravity);
} else if (x > 1) {
xPos -= (x * gravity);
}
if (y > 1) {
yPos += (y * gravity);
} else if (y < -1) {
yPos -= (-y * gravity);
}
}
This is the closest I got by myself.
By the way the x and y values are from the accelerometer.
Any help would be much appreciated, thanks!
I think you'll need 3 things for this, forces (x and y, which you have), velocities (call them xVel and yVel) and positions (xPos and yPos which you also have). The position of the ball is updated (in the simplest way) by:
xPos += dt*xVel;
yPos += dt*yVel;
xVel += dt*x;
yVel += dt*y;
The variable 'dt' is the timestep, which controls how fast the ball will move. If this is set too large, though, the program will be unstable! Try dt = 0.001 or so to start and gradually set it higher.
Then, to get the ball to reflect from the walls, just flip the velocity if it hits a wall:
if (xPos > xMax) {
xPos = xMax;
xVel *= -1.0;
} else if (xPos < 0.0) {
xPos = 0.0;
xVel *= -1.0;
}
and the same for y. The 'xPos = ...' is just to stop the ball going off the edge of the screen. If you'd like the ball to bounce a little less every time it hits a wall, change the '-1.0' to '-0.9' or something (this is the coefficient of restitution).
Hopefully that should be all. Good luck!
Some comments on your actual code:
Both of these lines do exactly the same thing:
yPos -= (-y * gravity);
yPos += (y * gravity);
likewise, both of these lines do the same thing:
xPos += (-x * gravity);
xPos -= (x * gravity);
You are not handling the cases -1 <= x <= 1 or -1 <= y <= 1
Some comments on the concepts:
Start with one ball.
You want the ball to have two separate velocities (both either positive or negative), one in the x-direction and one in the y-direction. Every frame, add those velocities to the ball's position.
Then add collision detection. Check each frame if the center of the ball is outside the screen. If it is, make it bounce (the easiest way to do this is to make the ball's y-velocity positive when it goes off the most-negative y-value displayed on the screen; and do similarly for the other screen-edges).
Improve the collision detection: see if you can figure out how to check if any part of the ball it outside the screen (hint: to check if the ball is off the left-side of the screen, you only need to check the left-most coordinate of the ball)
Then take gravity into consideration - gravity will be a constant number subtracted from the y-velocity every frame, until the y-velocity hits 0.
Youtube video
Source code available at github.
Your x coordinate should not be affected by gravity.
I would advise against using accelerometers until you understand how the physics work for the simple case.
Break it up into stages. First, ensure that you can get the ball falling at a constant velocity.
Once you have that working, worry about the gravity causing the velocity to increase.
Once you have that working, worry about the intersection with walls and velocity changes. A simple algorithm for bouncing off the walls would be to multiply the x velocity by -1 when you hit a vertical wall, and multiply the y velocity by -1 when you hit a horizontal wall.