epsilon awareness in collision detection

This commit is contained in:
Mike Schwörer 2017-04-26 20:22:01 +02:00
parent b1eeed1119
commit dc6aa75f20
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
5 changed files with 5303 additions and 5261 deletions

File diff suppressed because it is too large Load Diff

View File

@ -138,7 +138,13 @@ public class CRGameRenderer extends AbstractGameRenderer {
renderDebug("CameraOffset: (" + (int)cam.position.x + "|" + (int)cam.position.y + ")");
renderDebug("Player(x): " + (int)gameworld.player.bounds.x);
renderDebug(
"Player(x): " + (int)gameworld.player.bounds.x +
" | Touch [T;L;B;R] := [" + (gameworld.player.isTouching_TOP()?1:0) +
";" + (gameworld.player.isTouching_LEFT()?1:0) +
";" + (gameworld.player.isTouching_BOTTOM()?1:0) +
";" + (gameworld.player.isTouching_RIGHT()?1:0) + "]");
if (gameworld.mapprovider instanceof EndlessMapProvider)
renderDebug("Procedural Piece: \"" + ((EndlessMapProvider)gameworld.mapprovider).getCurrentSection(gameworld.player.bounds).piece_name + "\"");
else if (gameworld.mapprovider instanceof StaticMapProvider)

View File

@ -7,7 +7,6 @@ import com.badlogic.gdx.math.Vector2;
import de.samdev.colorrunner.game.world.CRGameWorld;
public abstract class CRGameEntity {
public final static float F_EPSILON = 0.00004f;
protected CRGameWorld world;

View File

@ -9,6 +9,10 @@ import de.samdev.colorrunner.game.world.CRGameWorld;
public abstract class MovingEntity extends CRGameEntity {
protected Vector2 velocity = new Vector2();
public final static float COLL_PUSHBACK_EPSILON = 1e-5F;
public final static float COLL_DETECT_EPSILON = 2e-5f;
public final static float EXPAND_EPSILON = 1e-6f;
private boolean face_TOP_isTouching = false;
private boolean face_LEFT_isTouching = false;
private boolean face_BOTTOM_isTouching = false;
@ -24,41 +28,49 @@ public abstract class MovingEntity extends CRGameEntity {
protected boolean moveByX(float bx) {
if (bx == 0) return false;
boolean collided = false;
Rectangle original = new Rectangle(bounds);
bounds.x += bx;
Rectangle next = new Rectangle(bounds);
next.x += bx;
next.set(next.x - COLL_PUSHBACK_EPSILON, next.y - COLL_PUSHBACK_EPSILON, next.width + 2*COLL_PUSHBACK_EPSILON, next.height + 2*COLL_PUSHBACK_EPSILON);
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (ent.bounds.overlaps(bounds) && ent.canCollide(true, this)) {
if (ent.bounds.overlaps(original)) {
if (ent.bounds.overlaps(next) && ent.canCollide(true, this)) {
if (ent.bounds.overlaps(bounds)) {
Gdx.app.log("Collision", "Ignore in bounds collision");
continue;
}
if (bx < 0) { // LEFT
float correction = (ent.bounds.x + ent.bounds.width) - bounds.x;
bounds.x += correction;
if (bx < 0) { // DOWN
float correction = (ent.bounds.x + ent.bounds.height) - next.x;
next.x += correction;
bx += correction;
if (bx >= -COLL_PUSHBACK_EPSILON)
return true; // collision but no movement
collided = true;
} else { // RIGHT
float correction = ent.bounds.x - (bounds.x + bounds.width);
bounds.x += correction;
} else { // UP
float correction = ent.bounds.x - (next.x + next.height);
next.x += correction;
bx += correction;
if (bx <= COLL_PUSHBACK_EPSILON)
return true; // collision but no movement
collided = true;
}
}
}
bounds.x += bx;
return collided;
}
@ -67,38 +79,46 @@ public abstract class MovingEntity extends CRGameEntity {
boolean collided = false;
Rectangle original = new Rectangle(bounds);
bounds.y += by;
Rectangle next = new Rectangle(bounds);
next.y += by;
next.set(next.x - COLL_PUSHBACK_EPSILON, next.y - COLL_PUSHBACK_EPSILON, next.width + 2*COLL_PUSHBACK_EPSILON, next.height + 2*COLL_PUSHBACK_EPSILON);
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (ent.bounds.overlaps(bounds) && ent.canCollide(true, this)) {
if (ent.bounds.overlaps(next) && ent.canCollide(true, this)) {
if (ent.bounds.overlaps(original)) {
if (ent.bounds.overlaps(bounds)) {
Gdx.app.log("Collision", "Ignore in bounds collision");
continue;
}
if (by < 0) { // DOWN
float correction = (ent.bounds.y + ent.bounds.height) - bounds.y;
bounds.y += correction;
float correction = (ent.bounds.y + ent.bounds.height) - next.y;
next.y += correction;
by += correction;
if (by >= -COLL_PUSHBACK_EPSILON)
return true; // collision but no movement
collided = true;
} else { // UP
float correction = ent.bounds.y - (bounds.y + bounds.height);
bounds.y += correction;
float correction = ent.bounds.y - (next.y + next.height);
next.y += correction;
by += correction;
if (by <= COLL_PUSHBACK_EPSILON)
return true; // collision but no movement
collided = true;
}
}
}
bounds.y += by;
return collided;
}
@ -110,56 +130,55 @@ public abstract class MovingEntity extends CRGameEntity {
}
private boolean updateHitBoxWidth(float width) {
Rectangle original = new Rectangle(bounds);
Rectangle next = new Rectangle(bounds);
float add = width - bounds.width;
if (isTouching_LEFT() && ! isTouching_RIGHT()) {
bounds.width += add;
bounds.x -= add;
next.width += add;
next.x -= add;
} else if (! isTouching_LEFT() && isTouching_RIGHT()) {
bounds.width += add;
next.width += add;
} else {
bounds.width += add;
bounds.x -= add/2;
next.width += add;
next.x -= add/2;
}
if (add < 0) {
bounds.set(next);
return true;
} else {
next.set(next.x - COLL_PUSHBACK_EPSILON, next.y - COLL_PUSHBACK_EPSILON, next.width + 2*COLL_PUSHBACK_EPSILON, next.height + 2*COLL_PUSHBACK_EPSILON);
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (ent.bounds.overlaps(bounds) && ent.canCollide(false, this)) {
if (ent.bounds.overlaps(original)) {
Gdx.app.log("Collision", "Ignore in bounds collision on HB Expand");
if (ent.bounds.overlaps(next) && ent.canCollide(false, this)) {
if (ent.bounds.overlaps(bounds)) {
Gdx.app.log("Collision", "Ignore in bounds collision on HB Expand [X]");
continue;
}
if (ent.bounds.x > bounds.x) {
bounds.x -= (bounds.x + bounds.width) - ent.bounds.x;
if (ent.bounds.x > next.x) {
next.x -= (next.x + next.width) - ent.bounds.x;
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
if (ent2.bounds.overlaps(bounds) && ent2.canCollide(false, this)) {
bounds = original;
if (ent2.bounds.overlaps(next) && ent2.canCollide(false, this)) {
Gdx.app.log("Entity Hitbox", "Expand failed X+");
return false;
}
}
} else if (ent.bounds.x < bounds.x) {
bounds.x += (ent.bounds.x + ent.bounds.width) - bounds.x;
} else if (ent.bounds.x < next.x) {
next.x += (ent.bounds.x + ent.bounds.width) - next.x;
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
if (ent2.bounds.overlaps(bounds) && ent2.canCollide(false, this)) {
bounds = original;
if (ent2.bounds.overlaps(next) && ent2.canCollide(false, this)) {
Gdx.app.log("Entity Hitbox", "Expand failed X-");
return false;
}
@ -168,61 +187,61 @@ public abstract class MovingEntity extends CRGameEntity {
}
}
bounds.set(next.x + COLL_PUSHBACK_EPSILON, next.y + COLL_PUSHBACK_EPSILON, next.width - 2*COLL_PUSHBACK_EPSILON, next.height - 2*COLL_PUSHBACK_EPSILON);
return true;
}
}
private boolean updateHitBoxHeight(float height) {
Rectangle original = new Rectangle(bounds);
Rectangle next = new Rectangle(bounds);
float add = height - bounds.height;
if (isTouching_BOTTOM() && ! isTouching_TOP()) {
bounds.height += add;
next.height += add;
} else if (! isTouching_BOTTOM() && isTouching_TOP()) {
bounds.height += add;
bounds.y -= add;
next.height += add;
next.y -= add;
} else {
bounds.height += add;
bounds.y -= add/2;
next.height += add;
next.y -= add/2;
}
if (add < 0) {
bounds.set(next);
return true;
} else {
next.set(next.x - COLL_PUSHBACK_EPSILON, next.y - COLL_PUSHBACK_EPSILON, next.width + 2*COLL_PUSHBACK_EPSILON, next.height + 2*COLL_PUSHBACK_EPSILON);
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (ent.bounds.overlaps(bounds) && ent.canCollide(false, this)) {
if (ent.bounds.overlaps(next) && ent.canCollide(false, this)) {
if (ent.bounds.overlaps(original)) {
Gdx.app.log("Collision", "Ignore in bounds collision on HB Expand");
if (ent.bounds.overlaps(bounds)) {
Gdx.app.log("Collision", "Ignore in bounds collision on HB Expand [Y]");
continue;
}
if (ent.bounds.y > bounds.y) {
bounds.y -= (bounds.y + bounds.height) - ent.bounds.y;
if (ent.bounds.y > next.y) {
next.y -= (next.y + next.height) - ent.bounds.y;
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
if (ent2.bounds.overlaps(bounds) && ent2.canCollide(false, this)) {
bounds = original;
if (ent2.bounds.overlaps(next) && ent2.canCollide(false, this)) {
Gdx.app.log("Entity Hitbox", "Expand failed Y+");
return false;
}
}
} else if (ent.bounds.y < bounds.y) {
bounds.y += (ent.bounds.y + ent.bounds.height) - bounds.y;
} else if (ent.bounds.y < next.y) {
next.y += (ent.bounds.y + ent.bounds.height) - next.y;
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
if (ent2.bounds.overlaps(bounds) && ent2.canCollide(false, this)) {
bounds = original;
if (ent2.bounds.overlaps(next) && ent2.canCollide(false, this)) {
Gdx.app.log("Entity Hitbox", "Expand failed Y-");
return false;
}
@ -231,6 +250,7 @@ public abstract class MovingEntity extends CRGameEntity {
}
}
bounds.set(next.x + COLL_PUSHBACK_EPSILON, next.y + COLL_PUSHBACK_EPSILON, next.width - 2*COLL_PUSHBACK_EPSILON, next.height - 2*COLL_PUSHBACK_EPSILON);
return true;
}
}
@ -244,19 +264,19 @@ public abstract class MovingEntity extends CRGameEntity {
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (Math.abs((ent.bounds.y + ent.bounds.height) - bounds.y) < F_EPSILON && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x && ent.canCollide(false, this)) {
if (Math.abs((ent.bounds.y + ent.bounds.height) - bounds.y) < COLL_DETECT_EPSILON && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x && ent.canCollide(false, this)) {
face_BOTTOM_isTouching = true;
}
if (Math.abs((bounds.y + bounds.height) - ent.bounds.y) < F_EPSILON && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x && ent.canCollide(false, this)) {
if (Math.abs((bounds.y + bounds.height) - ent.bounds.y) < COLL_DETECT_EPSILON && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x && ent.canCollide(false, this)) {
face_TOP_isTouching = true;
}
if (Math.abs((bounds.x + bounds.width) - ent.bounds.x) < F_EPSILON && ent.bounds.y < (bounds.y + bounds.height) && (ent.bounds.y + ent.bounds.height) > bounds.y && ent.canCollide(false, this)) {
if (Math.abs((bounds.x + bounds.width) - ent.bounds.x) < COLL_DETECT_EPSILON && ent.bounds.y < (bounds.y + bounds.height) && (ent.bounds.y + ent.bounds.height) > bounds.y && ent.canCollide(false, this)) {
face_RIGHT_isTouching = true;
}
if (Math.abs((ent.bounds.x + ent.bounds.width) - bounds.x) < F_EPSILON && ent.bounds.y < (bounds.y + bounds.height) && (ent.bounds.y + ent.bounds.height) > bounds.y && ent.canCollide(false, this)) {
if (Math.abs((ent.bounds.x + ent.bounds.width) - bounds.x) < COLL_DETECT_EPSILON && ent.bounds.y < (bounds.y + bounds.height) && (ent.bounds.y + ent.bounds.height) > bounds.y && ent.canCollide(false, this)) {
face_LEFT_isTouching = true;
}
}
@ -273,17 +293,33 @@ public abstract class MovingEntity extends CRGameEntity {
if (moveByX(velocity.x * delta)) {
velocity.x = 0;
}
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (ent.bounds.overlaps(bounds) && ent.canCollide(true, this))
Gdx.app.error("Collision", "Collision after move X");
}
}
if (velocity.y != 0) {
if (moveByY(velocity.y * delta)) {
velocity.y = 0;
}
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (ent.bounds.overlaps(bounds) && ent.canCollide(true, this))
Gdx.app.error("Collision", "Collision after Y");
}
}
updateTouchCollisions();
}
public boolean isTouching_ANY() {
return face_TOP_isTouching || face_LEFT_isTouching || face_BOTTOM_isTouching || face_RIGHT_isTouching;
}
public boolean isTouching_TOP() {
return face_TOP_isTouching;
}

View File

@ -73,37 +73,38 @@ public class PlayerEntity extends GravityEntity {
}
private void updateRotation(float delta) {
if (((!isTouching_BOTTOM() && getType == GetTriggerType.RUNBOTTOM)||(!isTouching_TOP() && getType == GetTriggerType.RUNTOP) )|| visualRotation % 90 != 0) {
if (! isTouching_BOTTOM() && getType == GetTriggerType.RUNBOTTOM) {
visualRotation = visualRotation + PLAYER_ROTATION_SPEED * delta;
visualRotation = (visualRotation + 720f) % 360f;
boolean touch = isTouching_ANY();
boolean aligned = (visualRotation % 90 == 0);
float p = (float) ((Math.max(Math.abs(Math.cos(Math.toRadians(visualRotation + 45))), Math.abs(Math.sin(Math.toRadians(visualRotation + 45)))) * Math.sqrt(PLAYER_WIDTH * PLAYER_WIDTH + PLAYER_HEIGHT * PLAYER_HEIGHT)));
updateHitBox(p, p);
}
else if (!isTouching_TOP() && getType == GetTriggerType.RUNTOP){
visualRotation = visualRotation + PLAYER_ROTATION_SPEED * delta;
visualRotation = (visualRotation + 720f) % 360f;
if (!touch)
{
visualRotation = visualRotation + PLAYER_ROTATION_SPEED * delta;
visualRotation = (visualRotation + 720f) % 360f;
float p = (float) ((Math.max(Math.abs(Math.cos(Math.toRadians(visualRotation + 45))), Math.abs(Math.sin(Math.toRadians(visualRotation + 45)))) * Math.sqrt(PLAYER_WIDTH * PLAYER_WIDTH + PLAYER_HEIGHT * PLAYER_HEIGHT)));
updateHitBox(p, p);
}
else {
visualRotation = (visualRotation + 720f) % 90f;
if (visualRotation < 45)
visualRotation = visualRotation + PLAYER_ROTATION_SPEED * delta;
else
visualRotation = visualRotation - PLAYER_ROTATION_SPEED * delta;
if (visualRotation >= 90f || visualRotation <= 0f)
visualRotation = 0f;
visualRotation = (visualRotation + 720f) % 90f;
float p = (float) ((Math.max(Math.abs(Math.cos(Math.toRadians(visualRotation + 45))), Math.abs(Math.sin(Math.toRadians(visualRotation + 45)))) * Math.sqrt(PLAYER_WIDTH * PLAYER_WIDTH + PLAYER_HEIGHT * PLAYER_HEIGHT)));
updateHitBox(p, p);
}
float p = (float) ((Math.max(Math.abs(Math.cos(Math.toRadians(visualRotation + 45))), Math.abs(Math.sin(Math.toRadians(visualRotation + 45)))) * Math.sqrt(PLAYER_WIDTH * PLAYER_WIDTH + PLAYER_HEIGHT * PLAYER_HEIGHT)));
updateHitBox(p, p);
}
else if (touch && aligned)
{
// all ok - move along
}
else if (touch && !aligned)
{
visualRotation = (visualRotation + 720f) % 90f;
if (visualRotation < 45) visualRotation = visualRotation + PLAYER_ROTATION_SPEED * delta;
else visualRotation = visualRotation - PLAYER_ROTATION_SPEED * delta;
if (visualRotation >= 90f || visualRotation <= 0f) visualRotation = 0f;
visualRotation = (visualRotation + 720f) % 90f;
float p = (float) ((Math.max(Math.abs(Math.cos(Math.toRadians(visualRotation + 45))), Math.abs(Math.sin(Math.toRadians(visualRotation + 45)))) * Math.sqrt(PLAYER_WIDTH * PLAYER_WIDTH + PLAYER_HEIGHT * PLAYER_HEIGHT)));
updateHitBox(p, p);
}
else
{
Gdx.app.error("PlayerEntity", "updateRotation::WTF");
}
}