epsilon awareness in collision detection
This commit is contained in:
parent
b1eeed1119
commit
dc6aa75f20
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user