ColorRunner/core/src/de/samdev/colorrunner/game/world/entities/MovingEntity.java

360 lines
9.2 KiB
Java
Raw Normal View History

2014-08-10 17:19:40 +02:00
package de.samdev.colorrunner.game.world.entities;
2014-08-10 20:59:50 +02:00
import com.badlogic.gdx.Gdx;
2014-08-11 00:24:55 +02:00
import com.badlogic.gdx.math.Rectangle;
2014-08-10 19:41:06 +02:00
import com.badlogic.gdx.math.Vector2;
import de.samdev.colorrunner.CRGame;
2014-08-10 19:41:06 +02:00
import de.samdev.colorrunner.game.world.CRGameWorld;
2014-08-10 17:19:40 +02:00
2014-08-10 19:41:06 +02:00
public abstract class MovingEntity extends CRGameEntity {
2017-04-30 19:53:36 +02:00
public Vector2 velocity = new Vector2();
2014-08-10 20:59:50 +02:00
2017-04-30 18:06:50 +02:00
public final static float EPSILON = 1e-4f;
public final static float EPSILON_TOUCH = 5e-4f;
private boolean face_TOP_isTouching = false;
private boolean face_LEFT_isTouching = false;
2014-08-10 20:59:50 +02:00
private boolean face_BOTTOM_isTouching = false;
private boolean face_RIGHT_isTouching = false;
2014-08-10 19:41:06 +02:00
public MovingEntity(CRGameWorld _owner, float width, float height) {
this(_owner, 0, 0, width, height);
}
2014-08-10 19:41:06 +02:00
public MovingEntity(CRGameWorld _owner, float x, float y, float width, float height) {
super(_owner, x, y, width, height);
2014-08-10 17:19:40 +02:00
}
2014-08-10 19:41:06 +02:00
protected boolean moveByX(float bx) {
if (bx == 0) return false;
2014-08-10 19:41:06 +02:00
boolean collided = false;
Rectangle next = new Rectangle(bounds);
next.x += bx;
2014-08-10 19:41:06 +02:00
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (overlaps(next, ent.bounds) && ent.canCollide(true, this)) {
if (overlaps(ent.bounds, bounds)) {
Gdx.app.log("Collision", "Ignore in bounds collision [X]");
2014-08-11 00:24:55 +02:00
continue;
}
if (bx < 0) { // LEFT
float correction = (ent.bounds.x + ent.bounds.height) - next.x + EPSILON;
next.x += correction;
bx = next.x - bounds.x;
if (bx >= 0) return true; // collision but no movement
2014-08-10 19:41:06 +02:00
collided = true;
} else { // RIGHT
float correction = (next.x + next.height) - ent.bounds.x + EPSILON;
next.x -= correction;
bx = next.x - bounds.x;
if (bx <= 0) return true; // collision but no movement
2014-08-10 20:59:50 +02:00
collided = true;
2014-08-10 19:41:06 +02:00
}
}
}
bounds.x = next.x;
2014-08-10 19:41:06 +02:00
return collided;
}
2014-08-10 19:41:06 +02:00
protected boolean moveByY(float by) {
if (by == 0) return false;
2014-08-10 19:41:06 +02:00
boolean collided = false;
2014-08-11 00:24:55 +02:00
Rectangle next = new Rectangle(bounds);
next.y += by;
2017-04-26 20:36:26 +02:00
2014-08-10 19:41:06 +02:00
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (overlaps(next, ent.bounds) && ent.canCollide(true, this)) {
if (overlaps(bounds, ent.bounds)) {
Gdx.app.log("Collision", "Ignore in bounds collision [Y]");
2014-08-11 00:24:55 +02:00
continue;
}
2014-08-10 19:41:06 +02:00
if (by < 0) { // DOWN
float correction = (ent.bounds.y + ent.bounds.height) - next.y + EPSILON;
next.y += correction;
by = next.y - bounds.y;
if (by >= 0) return true; // collision but no movement
2014-08-10 19:41:06 +02:00
collided = true;
} else { // UP
float correction = (next.y + next.height) - ent.bounds.y + EPSILON;
next.y -= correction;
by = next.y - bounds.y;
if (by <= 0) return true; // collision but no movement
2014-08-10 20:59:50 +02:00
collided = true;
2014-08-10 19:41:06 +02:00
}
}
}
bounds.y = next.y;
2014-08-10 19:41:06 +02:00
return collided;
}
2014-08-11 15:36:03 +02:00
protected boolean updateHitBox(float width, float height) {
boolean succ_x = updateHitBoxWidth(width);
if (CRGame.DEBUG && getFirstCollision() != null) Gdx.app.error("Collision", "Collision after updateHitBox [[Width]] - " + succ_x);
2014-08-11 15:36:03 +02:00
boolean succ_y = updateHitBoxHeight(height);
if (CRGame.DEBUG && getFirstCollision() != null) Gdx.app.error("Collision", "Collision after updateHitBox [[Height]] - " + succ_y);
2014-08-11 15:36:03 +02:00
return succ_x && succ_y;
}
2014-08-11 15:36:03 +02:00
private boolean updateHitBoxWidth(float width) {
Rectangle next = new Rectangle(bounds);
2014-08-11 15:36:03 +02:00
float add = width - bounds.width;
2014-08-11 15:36:03 +02:00
if (isTouching_LEFT() && ! isTouching_RIGHT()) {
next.width += add;
2014-08-11 15:36:03 +02:00
} else if (! isTouching_LEFT() && isTouching_RIGHT()) {
next.width += add;
2017-04-30 18:06:50 +02:00
next.x -= add;
next.x -= EPSILON;
2014-08-11 15:36:03 +02:00
} else {
next.width += add;
next.x -= add/2;
2014-08-11 15:36:03 +02:00
}
2014-08-11 15:36:03 +02:00
if (add < 0) {
2017-04-30 18:06:50 +02:00
if (CRGame.DEBUG) {
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
2017-04-30 18:06:50 +02:00
if (overlaps(next, ent2.bounds) && ent2.canCollide(false, this)) {
Gdx.app.log("Entity Hitbox", "Expand failed X- || WTF");
return false;
}
}
}
2017-04-30 18:06:50 +02:00
bounds.set(next);
2014-08-11 15:36:03 +02:00
return true;
} else {
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (overlaps(ent.bounds, next) && ent.canCollide(false, this)) {
if (overlaps(ent.bounds, bounds)) {
Gdx.app.log("Collision", "Ignore in bounds collision on HB Expand [X]");
2014-08-11 15:36:03 +02:00
continue;
}
if (ent.bounds.x > next.x) {
next.x -= (next.x + next.width) - ent.bounds.x + EPSILON;
2014-08-11 15:36:03 +02:00
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
if (overlaps(next, ent2.bounds) && ent2.canCollide(false, this)) {
2014-08-11 15:36:03 +02:00
Gdx.app.log("Entity Hitbox", "Expand failed X+");
return false;
}
}
} else if (ent.bounds.x < next.x) {
next.x += (ent.bounds.x + ent.bounds.width) - next.x + EPSILON;
2014-08-11 15:36:03 +02:00
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
if (overlaps(next, ent2.bounds) && ent2.canCollide(false, this)) {
2014-08-11 15:36:03 +02:00
Gdx.app.log("Entity Hitbox", "Expand failed X-");
return false;
}
}
}
}
}
2017-04-26 20:36:26 +02:00
bounds.set(next);
2014-08-11 15:36:03 +02:00
return true;
}
}
2014-08-11 15:36:03 +02:00
private boolean updateHitBoxHeight(float height) {
Rectangle next = new Rectangle(bounds);
2014-08-11 15:36:03 +02:00
float add = height - bounds.height;
if (isTouching_BOTTOM() && !isTouching_TOP()) {
next.height += add;
2014-08-11 15:36:03 +02:00
} else if (! isTouching_BOTTOM() && isTouching_TOP()) {
next.height += add;
2017-04-30 18:06:50 +02:00
next.y -= add;
next.y -= EPSILON;
2014-08-11 15:36:03 +02:00
} else {
next.height += add;
next.y -= add/2;
2014-08-11 15:36:03 +02:00
}
2014-08-11 15:36:03 +02:00
if (add < 0) {
2017-04-30 18:06:50 +02:00
if (CRGame.DEBUG) {
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
2017-04-30 18:06:50 +02:00
if (overlaps(next, ent2.bounds) && ent2.canCollide(false, this)) {
Gdx.app.log("Entity Hitbox", "Expand failed Y- || WTF");
return false;
}
}
}
2017-04-30 18:06:50 +02:00
bounds.set(next);
2014-08-11 15:36:03 +02:00
return true;
} else {
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (overlaps(ent.bounds, next) && ent.canCollide(false, this)) {
if (overlaps(ent.bounds, bounds)) {
Gdx.app.log("Collision", "Ignore in bounds collision on HB Expand [Y]");
2014-08-11 15:36:03 +02:00
continue;
}
if (ent.bounds.y > next.y) {
next.y -= (next.y + next.height) - ent.bounds.y + EPSILON;
2014-08-11 15:36:03 +02:00
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
if (overlaps(next, ent2.bounds) && ent2.canCollide(false, this)) {
2014-08-11 15:36:03 +02:00
Gdx.app.log("Entity Hitbox", "Expand failed Y+");
return false;
}
}
} else if (ent.bounds.y < next.y) {
next.y += (ent.bounds.y + ent.bounds.height) - next.y + EPSILON;
2014-08-11 15:36:03 +02:00
for (CRGameEntity ent2 : world.entities) {
if (ent2 == this) continue;
if (overlaps(next, ent2.bounds) && ent2.canCollide(false, this)) {
2014-08-11 15:36:03 +02:00
Gdx.app.log("Entity Hitbox", "Expand failed Y-");
return false;
}
}
}
}
}
bounds.set(next);
2014-08-11 15:36:03 +02:00
return true;
}
}
2014-08-10 20:59:50 +02:00
private void updateTouchCollisions() {
face_TOP_isTouching = false;
face_LEFT_isTouching = false;
face_BOTTOM_isTouching = false;
face_RIGHT_isTouching = false;
2014-08-10 19:41:06 +02:00
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
2017-04-26 20:36:26 +02:00
if (Math.abs((ent.bounds.y + ent.bounds.height) - bounds.y) < EPSILON_TOUCH && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x && ent.canCollide(false, this)) {
2014-08-10 20:59:50 +02:00
face_BOTTOM_isTouching = true;
}
2017-04-26 20:36:26 +02:00
if (Math.abs((bounds.y + bounds.height) - ent.bounds.y) < EPSILON_TOUCH && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x && ent.canCollide(false, this)) {
2014-08-10 20:59:50 +02:00
face_TOP_isTouching = true;
2014-08-10 19:41:06 +02:00
}
2017-04-26 20:36:26 +02:00
if (Math.abs((bounds.x + bounds.width) - ent.bounds.x) < EPSILON_TOUCH && ent.bounds.y < (bounds.y + bounds.height) && (ent.bounds.y + ent.bounds.height) > bounds.y && ent.canCollide(false, this)) {
2014-08-11 00:24:55 +02:00
face_RIGHT_isTouching = true;
}
2017-04-26 20:36:26 +02:00
if (Math.abs((ent.bounds.x + ent.bounds.width) - bounds.x) < EPSILON_TOUCH && ent.bounds.y < (bounds.y + bounds.height) && (ent.bounds.y + ent.bounds.height) > bounds.y && ent.canCollide(false, this)) {
2014-08-11 00:24:55 +02:00
face_LEFT_isTouching = true;
}
2014-08-10 19:41:06 +02:00
}
2014-08-10 20:59:50 +02:00
}
2014-08-10 20:59:50 +02:00
public Vector2 getVelocity() {
return velocity;
2014-08-10 19:41:06 +02:00
}
2014-08-10 17:19:40 +02:00
@Override
public void update(float delta) {
2014-08-10 19:41:06 +02:00
if (velocity.x != 0) {
2014-08-10 20:59:50 +02:00
if (moveByX(velocity.x * delta)) {
velocity.x = 0;
}
if (CRGame.DEBUG && getFirstCollision() != null) Gdx.app.error("Collision", "Collision after move [[X]]");
2014-08-10 19:41:06 +02:00
}
2014-08-10 19:41:06 +02:00
if (velocity.y != 0) {
2014-08-10 20:59:50 +02:00
if (moveByY(velocity.y * delta)) {
velocity.y = 0;
}
if (CRGame.DEBUG && getFirstCollision() != null) Gdx.app.error("Collision", "Collision after move [[Y]]");
2014-08-10 19:41:06 +02:00
}
2014-08-10 20:59:50 +02:00
updateTouchCollisions();
}
private CRGameEntity getFirstCollision()
{
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (overlaps(ent.bounds, bounds) && ent.canCollide(false, this)) return ent;
}
return null;
}
2017-04-26 20:36:26 +02:00
private boolean overlaps (Rectangle r1, Rectangle r2) {
return r1.overlaps(r2) && r2.overlaps(r1); // make overlaps commutative (?)
2017-04-26 20:36:26 +02:00
}
public boolean isTouching_ANY() {
return face_TOP_isTouching || face_LEFT_isTouching || face_BOTTOM_isTouching || face_RIGHT_isTouching;
}
2014-08-10 20:59:50 +02:00
public boolean isTouching_TOP() {
return face_TOP_isTouching;
2014-08-10 17:19:40 +02:00
}
2014-08-10 20:59:50 +02:00
public boolean isTouching_LEFT() {
return face_LEFT_isTouching;
}
public boolean isTouching_BOTTOM() {
return face_BOTTOM_isTouching;
}
public boolean isTouching_RIGHT() {
return face_RIGHT_isTouching;
}
2014-08-10 17:19:40 +02:00
}