I am trying to make the player rotate and face a target position, and have encountered a problem I am unable to fix.
When the ship is rotating towards the target position, when it reaches directly below the target position, it goes crazy and moves left and right while going downwards (away from the target position) and eventually off the screen.
I do not want to limit the screen as it will look weird when it is slowly rotating if it is near the screen limit. In this case if I do limit the screen, it will still get stuck at the bottom as it keeps turning left and right. I uploaded an image on imgur to try to better explain my problem.
Yellow is the direction ship is moving (it is rotating towards the target position but will cross the red line), Green is an imaginary vector towards the target position, Red is another imaginary vector which represents whenever the ship goes crazy. When the green line goes on top of the red line, the ship begins to bug out and go out the screen.
public void ai() {
switch (playerArrived()) {
case 0:
break;
case 1:
pTargetPos.set(ran(0, Gdx.graphics.getWidth()), ran(0, Gdx.graphics.getHeight()));
System.out.println("Player Destination Reached: " + pPos.x + "," + pPos.y + " Moving to new point: " + pTargetPos.x + "," + pTargetPos.y);
break;
}
turn();
move();
ePlayerBody.set(pPos.x, pPos.y, pWidth);
}
public int playerArrived() {
if (pTargetPos.x < pPos.x + pWidth && pTargetPos.x > pPos.x - pWidth && pTargetPos.y < pPos.y + pHeight && pTargetPos.y > pPos.y - pHeight) {
return 1;
} else {
return 0;
}
}
public float ran(float low, float high) {
return (float) (Math.random() * (high - low + 1)) + low;
}
public void move() {
pVel.set(pTargetPos.x - pPos.x, pTargetPos.y - pPos.y);
pVel.nor();
pVel.x *= sMaxSpeed + Gdx.graphics.getDeltaTime();
pVel.y *= sMaxSpeed + Gdx.graphics.getDeltaTime();
pVel.setAngle(pNewRotation + 90);
pPos.add(pVel);
}
public void turn() {
pRotation = ((Math.atan2(pTargetPos.x - pPos.x, -(pTargetPos.y - pPos.y)) * 180.0d / Math.PI)+180.0f);
pNewRotation += (pRotation - pNewRotation) * Gdx.graphics.getDeltaTime();
System.out.println(pRotation+" "+pNewRotation);
}
I uploaded an image on imgur to try to better explain my problem
When your ship is directly below, and just a little bit to the left of, the target, then the call Math.atan2(pTargetPos.x - pPos.x, -(pTargetPos.y - pPos.y)) will return a value very close to π/2. When your ship is directly below, and just a little to the right of, the target, that call will return -π/2. As you cross the red line, your direction will flip-flop by 180 degrees (π radians). That's why it's "going crazy".
You need to figure out a better way to determine what angle you should turn towards. I'd offer suggestions on that, but I'm still unclear on exactly what behaviour you are expecting.
This worked for me:
public void turn() {
pRotation = ((tPos.y - pPos.y) / (tPos.x - pPos.x) * 180.0d / Math.PI);
pNewRotation += (pRotation - pNewRotation) * Gdx.graphics.getDeltaTime();
}
public void move() {
pVel.set(pDir);
pVel.nor();
pVel.x *= sMaxSpeed + Gdx.graphics.getDeltaTime();
pVel.y *= sMaxSpeed + Gdx.graphics.getDeltaTime();
pVel.setAngle(pNewRotation + 90);
pPos.add(pVel);
}
Related
I'm currently working on a Top-Down-Shooter and having some issues with collision.
My world is made of tiles (64x64). The tiles and the entities are rectangles. The player moves with a speed of e.g 2.74 (and not in pixels for smoother movement). But when it comes to the collision between the player (an entity) and a wall i have some issues. To check if there is a collision i take the current position of my player and his movement speed to calculate where his next position would be and if there is any collision. But i check every pixel on the way, so i cant skip an obstacle even if the movement speed is very high. Let's just say the players current position is X:200 Y:200 and he moves 2.74 Pixels a tick in the x direction. My game now checks if there is any collision at X:201 Y:200, X:202 Y:200 or X:202.74 Y:200 and if not moves the player to that position. If I now try to move the player further in the x direction and there is a wall 0.26 Pixels away the player wont move and leave a tiny gap. I tried to calculate the distance between player and wall and add this amount to the players position but for that I need to know which side of the wall the player hits. Also I want the player to be able to move up and down when the wall he hits is in front of him and the other way around.
Here is my collision method (in Java):
public static boolean collision(float ex, float ey, int width, int height) { // ex, ey would be the next position of the player
if (ex < 0 || ex + width > worldWidth || ey < 0 || ey + height > worldHeight) return true; // checks if this position is in the world
int firstTileX = (int) (ex / Tile.TILE_SIZE); // calculates tiles he could possible collide width
int firstTileY = (int) (ey / Tile.TILE_SIZE);
int lastTileX = (int) ((ex + width - 1) / Tile.TILE_SIZE);
int lastTileY = (int) ((ey + height - 1) / Tile.TILE_SIZE);
for (int y = firstTileY; y <= lastTileY; y++) {
if (y < 0) continue; // checks for out of bounds
if (y >= worldTileHeight) break;
for (int x = firstTileX; x <= lastTileX; x++) {
if (x < 0) continue;
if (x >= worldTileWidth) break;
if (tiles[y][x].solid) return true; // if the tile is solid -> collision found
}
}
return false; // no collision found
}
And my movement method:
public void move(float xa, float ya) {
float nx, ny;
while (xa != 0 || ya != 0) {
nx = x;
ny = y;
if (xa != 0) {
if (Math.abs(xa) > 1) { // if the x-speed is greater than 1
nx = x + MathUtil.abs(xa); // returns -1 for negative numbers and 1 for positiv
xa -= MathUtil.abs(xa);
} else { // less than 1
nx = x + xa;
xa = 0;
}
}
if (ya != 0) { // same here
if (Math.abs(ya) > 1) {
ny = y + MathUtil.abs(ya);
ya -= MathUtil.abs(ya);
} else {
ny = y + ya;
ya = 0;
}
}
if (!Level.collision(nx, ny, width, height)) setPosition(nx, ny); // checks if there is an collision and sets the new position if not
else if (!Level.collision(nx, y, width, height)) x = nx; // if there was a collision check if the player can walk in x direction
else if (!Level.collision(x, ny, width, height)) y = ny; // or in y direction
}
}
My problem is the pretty much the same as CoderMusgrove's problem in his post (Pixel-perfect collision and doubles):
Summary & Question
I have a problem where if the speed of an entity isgreater thanthe distance from the tile it is going into, it will leave at least a pixel in between itself and the tile, and I really don't like this. What kind of algorithm could I use that will find the tiniest difference between the entity and the tile?
If you need any additional information, I will be glad to add it.
Thanks for your help!
Easily resolvable by changing your interpretation.
You are retaining a fractional position for the purpose of fine grained speed. Ignore the fraction for the purpose of collision detection and display (if you were to do sub-pixel rendering, do the collision on the subpixel rendering accurarcy level).
int screenX = (int) Math.round(objX);
int screenY = (int) Math.round(objY);
// rendering and collision detection based on rounded position
I've been trying from hours to setup gravity and relate it to time or what we call frame independent bounce ball. I did everything correct I guess, and I tried to implement the system where height of ball would decrease after every bounce. I did not even start that, and my code is creating something absurd I don't understand why. Here's my code:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentFrame = System.currentTimeMillis();
dt = currentFrame - lastFrame;
dt = dt/1000;
lastFrame = currentFrame;
myFreakinRect.set(0,0, canvas.getWidth(), canvas.getHeight());
freakinRed.setColor(Color.RED);
freakinRed.setStyle(Paint.Style.FILL);
canvas.drawRect(myFreakinRect, freakinRed);
//
// o yuea
if(goingDown) {
//velocityY = Math.sqrt(100 + 2*gravity*(posY));
velocityY = gravity*(currentFrame - runTime);
} else {
velocityY = downV - gravity*(currentFrame - runTime);
}
if(posX > w - ballRadius*2) {
goingRight = false;
}
if(posX < 0) {
goingRight = true;
}
if(posY > h - ballRadius*2) {
//initY = initY - 0.25F;
//if(initY < 0) initY = 0;
Log.i("xxx", String.valueOf(initY));
runTime = currentFrame;
downV = velocityY;
goingDown = false;
}
if(velocityY <= 0) {
goingDown = true;
runTime = currentFrame;
}
if(goingDown) posY += velocityY*dt;
else posY -= velocityY*dt;
if(goingRight) posX += velocityX*dt;
else posX -= velocityX*dt;
canvas.drawText(String.valueOf(posX)+" "+String.valueOf(posY), 10, 10, new Paint());
canvas.drawBitmap(rBall, (float)posX, (float)posY, myFreakingFaintPaint);
invalidate();
}
Here's a GIF what is happening:
UPDATE:
Here's my updated code which is clean, understandable and works perfect:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentFrame = System.currentTimeMillis();
dt = currentFrame - lastFrame;
dt = dt/1000;
lastFrame = currentFrame;
velocityY = downV + gravity*(currentFrame - runTime);
posY += velocityY*dt;
posX += velocityX*dt;
if(posX > w - ballRadius*2 || posX < 0) {
velocityX = -velocityX;
}
if(posY >= h - ballRadius*2) {
posY = h - ballRadius*2 - 2;
runTime = currentFrame;
downV = -0.8*velocityY;
}
canvas.drawBitmap(rBall, (float)posX, (float)posY, null);
invalidate();
}
Here ...
if(goingDown) {
//velocityY = Math.sqrt(100 + 2*gravity*(posY));
velocityY = gravity*(currentFrame - runTime);
} else {
velocityY = downV - gravity*(currentFrame - runTime);
}
... you update the velocity (speed, actually) assuming that the ball will not bounce during this frame.
Then here ...
if(posY > h - ballRadius*2) {
//initY = initY - 0.25F;
//if(initY < 0) initY = 0;
Log.i("xxx", String.valueOf(initY));
runTime = currentFrame;
downV = velocityY;
goingDown = false;
}
... you have not yet updated posY, so you are determining whether the ball hit the floor as a result of the previous update. If it did, you reverse the direction of motion, but keep the speed you already computed for this frame. As a result, each time the ball bounces, its initial upward speed is one frame's worth of acceleration greater than the speed it was traveling when it hit the floor.
You have a similar effect at the top of the ball's motion, but it's smaller because the speed is small there.
There are a couple of ways you might solve this problem. The simplest is probably to perform the bounce check after the position update instead of before.
Additional notes:
use the signs of your X and Y speeds instead of separate direction-of-motion flags (thus making the names velocityY etc. accurate). Your code will be simpler, and you'll need to handle only one change of vertical direction, not two, because the equations of motion will handle the other automatically.
you have a bit of a precision problem because you assume that the ball travels in the same direction for a whole frame. This may become noticeable if you allow the ball to reach high speeds: it will appear to penetrate the floor before bouncing back up.
this computation is suspicious: dt = dt/1000. Since dt seems to be computed from System.currentTimeMillis(), I am inclined to guess that it, too, has type long. In that case, you are performing an integer division and thereby losing precision.
In general:
Split up into model and view. In that case the rendering still runs fine, because the calculations are pretty light-weight, but you shouldn't run code inside the rendering-routine that isn't directly related to painting something.
Next point:
Stay as close to reality as possible, if you simulate physics. You can always optimize afterwards, but first make sure your code is actually doing what it's supposed to do. I'm currently playing a bit around with projectile-motion, so I've got a basic idea of what the code is supposed to do. I've been attempting to understand yout code for 10 mins so far. Interim result: I'm confused and don't quite get it.
My suggestion:
Start off with clearer code and try to stick as close to physical rules as possible. This code isn't optimized as far as it could be, but it's readable, understandable and simulates close enough to the real life. That makes it a lot simpler to debug:
final double GRAVITY = -9.81;
final double BALL_ELASTICITY = 0.95;
double vx, vy;
double x, y;
//dt is delta-time in seconds!!!
void simulateBall(double dt){
//calculate when the ball will touch the floor the next time
double next_floor_touch = (-vy + Math.sqrt(vy * vy - 2 * GRAVITY * y)) / GRAVITY;
double after_floor_touch = dt - next_floor_touch;
boolean touches_floor = (next_floor_touch <= dt);
//calculate new y
if(touches_floor){
//predict the speed the ball will have, after it bounces from the floor
double vy_at_floor = vy + GRAVITY * next_floor_touch;
double vy_from_floor = vy_at_floor * (-1) * BALL_ELASTICITY;
//predict y starting from the floor at t = next_floor_touch until dt
y = 0 + vy_from_floor * after_floor_touch + 0.5 * GRAVITY * after_floor_touch * after_floor_touch;
}else{
//uniform acceleration
y = y + vy * dt + 0.5 * GRAVITY * dt * dt;
}
//calculate vy
if(touches_floor){
//see above
double vy_after_floor = (vy + GRAVITY * next_floor_touch) * (-1) * BALL_ELASTICITY;
vy = vy_after_floor + GRAVITY * after_floor_touch;
}else{
vy = vy + GRAVITY * dt;
}
... //that's already the hardest part
}
This uses the quadratic equation to predict when the ball will hit the floor and uniform acceleration to calculate the position from a given position, speed and acceleration. Unless I've made any mistakes in my calculation (this code is not tested), this should be physically precise. BALL_ELASTICITY represents how much of the speed is left, after the ball hits the floor. That's not physically precise - might be, IDK - , but should do for this purpose.
I am simply trying to make a generic way to check for collision between rectangles. If you hit the top of a rectangle stop moving. However my rectangles seem to stop regardless of the x coordinate. I have three rectangles falling and one static rectangle that doesn't move, they are supposed to all fall on top of this static rectangle however when I run the code this happens.
Here is the collision handler code
float distanceY, furthestLeft;
public void update() {
for (int i = 0; i < stageObjects.size(); i++) {
iPos = stageObjects.get(i).getPosition();
iDim = stageObjects.get(i).getDimensions();
for(int k = 0; k < stageObjects.size(); k++){
kPos = stageObjects.get(k).getPosition();
kDim = stageObjects.get(k).getDimensions();
if(k == i){
continue;
}
distanceY = assignDy();
furthestLeft = assignDx();
//DistanceY is the subtraction of one objects y coordinate and
// if it equals 0 then they are colliding and the furthest left
// is the bottom right x coord of the furthest left objects x
//coord so it should check if this x is contained within an
// objects left x coord to right x coord
if(distanceY <= 1 && distanceY >= 0 && furthestLeft >= iPos.x && furthestLeft <= iPos.x + iDim.x){
stageObjects.get(i).react(Tuples.HIT_BOTTOM);
stageObjects.get(k).react(Tuples.HIT_FROM_TOP);
System.out.println("Collision: " + stageObjects.get(i).toString() + " with " +
stageObjects.get(k).toString());
}
}
}
}
}
public float assignDy(){
if(kPos.y > iPos.y){
return Math.abs(kPos.y - (iPos.y + iDim.y));
}else
return Math.abs(iPos.y - (kPos.y + kDim.y));
}
public float assignDx(){
if(kPos.x > iPos.x){
return kPos.x + kDim.x;
}else
return iPos.x + iDim.x;
}
The error lies here and here is the react method
public void react(int occurence){
if(occurence == Tuples.HIT_BOTTOM){
velocity.y = 0;
}
}
However, if they are further apart the code works perfectly look.
I have also noticed that the rectangle can fall through other rectangles if it is to the left of another rectangle, but if it further to the right at all it will get hung as if it landed on the rectangle. The only reason the above image worked is because the furthest right fell first, anything to the left to another rectangle will get hung as well if it falls after the rectangle to the left
I just don't see what exactly I am doing wrong any help is greatly appreciated!
Change iPos in the condition to something more generic, assign a variable like assignDx and dy to ccheck if Ipos or kPos is what you need to check for like so
public void assignOx(Vector2 ox){
if(kPos.x > iPos.x){
ox.x = iPos.x;
ox.y = iPos.x + iDim.x;
}else{
ox.x = kPos.x;
ox.y = kPos.x + kDim.x;
}
}
I am having a problem with collision detection on a tile based level at lower framerates. I have a platform game built in Java with the LibGdx game engine. At a framerate of 60 the game runs fine, but if I try it a 30 FPS the character will fall through tiles when it comes down from a jump.
I figured out that the character moved too fast. I already added something to check if there are any tiles that the character already passed, see "// 1" to "// 1 end" in the comments. I don't think it really helped because the problem still occurs.
Falling through the tiles seems to happen when the character hits corners of tiles, although I am not sure about it. It does not happen on a flat ground. Here is a picture of the problem (left is wrong, right is how it should be):
Again, the problem only happens at lower framerates. I am not sure what I have to change in my code. What am I missing in my code? Or do I have to use a different algorithm?
Here are the most important parts of the collision detection code. collisionY check collisions on the y-axis, collisionX on the x-axis. CheckTiles(checkX) helps finding the tiles that are should be checked (checkX is true if x-axis is checked, if false y-axis is checked):
protected boolean collisionY(Rectangle rect) {
int[] bounds = checkTiles(false);
Array<Rectangle> tiles = world.getTiles(bounds[0], bounds[1], bounds[2], bounds[3]);
rect.y += velocity.y;
if(velocity.y < 0 ) {
grounded = false;
}
for (Rectangle tile : tiles) {
if (rect.overlaps(tile)) {
if (velocity.y > 0) {
this.setY(tile.y - this.getHeight());
}
else {
// 1 Check if there are tiles above
Rectangle r = null;
int i = 1;
Rectangle r1 = null;
do {
r1 = r;
r = world.getTile(tile.x, tile.y + i);
i++;
} while (r != null);
if(r1 != null) {
this.setY(r1.y + r1.height);
}
// 1 end
else {
this.setY(tile.y + tile.height);
}
hitGround();
}
return true;
}
}
}
protected boolean collisionX(Rectangle rect) {
int[] bounds = checkTiles(true);
Array<Rectangle> tiles = world.getTiles(bounds[0], bounds[1], bounds[2], bounds[3]);
rect.x += velocity.x;
for (Rectangle tile : tiles) {
if (rect.overlaps(tile)) {
return true;
}
}
return false;
}
protected int[] checkTiles(boolean checkX) {
int startX, startY, endX, endY;
if(checkX) {
if (velocity.x > 0) {
startX = endX = (int) (this.getX() + this.getWidth() + velocity.x);
}
else {
startX = endX = (int) (this.getX() + velocity.x);
}
startY = (int) (this.getY());
endY = (int) (this.getY() + this.getHeight());
}
else {
if (velocity.y > 0) {
startY = endY = (int) (this.getY() + this.getHeight() + velocity.y); //
}
else {
startY = endY = (int) (this.getY() + velocity.y);
}
startX = (int) (this.getX());
endX = (int) (this.getX() + this.getWidth());
}
return new int[]{startX, startY, endX, endY};
}
The "Check if tile above" code will only be run if A) rect.overlaps(tile) was true, and B) velocity.y > 0 was false. I suspect in the case you care about, this code is simply not being executed. The character is either not overlapping the tile, so the check for tiles above doesn't occur, or its velocity is not such that the check occurs. However, I don't completely understand how the velocity works (how do up and down correspond to positive and negative values?) and I'm not familiar with this game engine.
I would try a different approach, though. Instead of first moving the character (which is what I think the line rect.y += velocity.y; does), and then trying to check if it has gone too far or passed through a tile, I would take the direction in which it's moving (velocity.y) and look for the first tile that it would hit going that way. If there is one, then place the character on that tile. If there's nothing in its way for velocity.y units in that direction, then it gets to move the whole distance in this time slice.
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.