Ghosts can chase!

Refactor position to be a Vector2 for convenience sake.
This commit is contained in:
Matt Low 2019-12-26 03:38:14 +04:00
parent d10148ace1
commit 75d0c41bb5
9 changed files with 163 additions and 45 deletions

View File

@ -1,8 +1,45 @@
package com.me.pacman.entity;
import com.badlogic.gdx.math.Vector2;
public enum Direction {
UP,
DOWN,
LEFT,
RIGHT,
;
public boolean isOpposite(Direction dir) {
switch (this) {
case UP:
return dir == DOWN;
case DOWN:
return dir == UP;
case LEFT:
return dir == RIGHT;
case RIGHT:
return dir == LEFT;
}
return false;
}
public Vector2 getVector(float scale) {
switch (this) {
case UP:
return new Vector2(0f, 1f * scale);
case DOWN:
return new Vector2(0f, -1f * scale);
case LEFT:
return new Vector2(-1 * scale, 0f);
case RIGHT:
return new Vector2(1 * scale, 0f);
}
return null;
}
public Vector2 getVector() {
return this.getVector(1f);
}
}

View File

@ -2,28 +2,27 @@ package com.me.pacman.entity;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.state.LevelState;
public abstract class Entity {
public LevelState state;
public float x;
public float y;
public Vector2 pos;
public int age;
public Entity(LevelState state, float x, float y) {
this.state = state;
this.x = x;
this.y = y;
this.pos = new Vector2(x, y);
this.age = 0;
}
public void render(SpriteBatch batch, int offsetX, int offsetY) {
batch.draw(getSprite(), (int) (x * 8) + (offsetX - 8), (y * 8) + (offsetY - 8));
batch.draw(getSprite(), (int) (pos.x * 8) + (offsetX - 8), (pos.y * 8) + (offsetY - 8));
}
public boolean onSameTile(Entity other) {
return (int) x == (int) other.x && (int) y == (int) other.y;
return (int) pos.x == (int) other.pos.x && (int) pos.y == (int) other.pos.y;
}
public abstract TextureRegion getSprite();

View File

@ -2,12 +2,16 @@ package com.me.pacman.entity;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.entity.ai.Behaviour;
import com.me.pacman.entity.ai.Target;
import com.me.pacman.level.LevelTile;
import com.me.pacman.state.PlayState;
public class Ghost extends MovableEntity {
public static final Direction[] GHOST_ORDER = { Direction.UP, Direction.LEFT, Direction.DOWN, Direction.RIGHT };
private PlayState state;
public Behaviour currentBehaviour;
@ -23,7 +27,7 @@ public class Ghost extends MovableEntity {
public Ghost(PlayState state, float x, float y, Direction direction, int spriteIndex,
Behaviour chaseBehaviour, Behaviour freightBehaviour, Behaviour scatterBehaviour) {
super(state, x, y, 7.33f, true, direction, 0.1f);
super(state, x, y, 7.03f, true, direction, 0.1f);
this.state = state;
this.spriteIndex = spriteIndex;
this.chaseBehaviour = chaseBehaviour;
@ -42,7 +46,7 @@ public class Ghost extends MovableEntity {
super.render(batch, offsetX, offsetY);
// draw eyes so the ghost can see
batch.draw(sprite[1][currDirection.ordinal()], (int) (x * 8) + (offsetX - 8), (y * 8) + (offsetY - 8));
batch.draw(sprite[1][currDirection.ordinal()], (int) (pos.x * 8) + (offsetX - 8), (pos.y * 8) + (offsetY - 8));
}
@Override
@ -52,6 +56,41 @@ public class Ghost extends MovableEntity {
if (!state.paused && age % 20 == 0) {
counter++;
}
if (currentBehaviour == null) {
return;
}
if (getNextDirection() != null) {
return;
}
Target target = currentBehaviour.getTarget();
Vector2 ahead = new Vector2((int) pos.x, (int) pos.y).add(currDirection.getVector());
Direction bestDirection = null;
float shortest = Float.MAX_VALUE;
for (Direction dir : GHOST_ORDER) {
if (dir.isOpposite(currDirection)) {
continue;
}
Vector2 adjacent = dir.getVector().add(ahead);
LevelTile nextTile = state.level.getTile(adjacent);
if (nextTile != null && nextTile.isPassable()) {
// compute distance to target
float d = target.distance_sqr(adjacent);
if (d < shortest) {
shortest = d;
bestDirection = dir;
}
}
}
if (bestDirection != null) {
setNextDirection(bestDirection);
}
}
private class ReturnToBase extends Behaviour {

View File

@ -1,5 +1,6 @@
package com.me.pacman.entity;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.level.LevelTile;
import com.me.pacman.state.LevelState;
@ -35,46 +36,46 @@ public abstract class MovableEntity extends Entity {
boolean turned = false;
switch (nextDirection) {
case UP:
nextTile = state.level.getTile(x, y + 1f);
nextTile = state.level.getTile(pos.x, pos.y + 1f);
if (nextTile == null) {
if (currDirection == Direction.DOWN) {
turned = true;
}
} else if (nextTile.isPassable() && Math.abs(x - ((int) x + 0.5f)) <= tolerance) {
x = ((int) x) + 0.5f;
} else if (nextTile.isPassable() && Math.abs(pos.x - ((int) pos.x + 0.5f)) <= tolerance) {
pos.x = ((int) pos.x) + 0.5f;
turned = true;
}
break;
case RIGHT:
nextTile = state.level.getTile(x + 1f, y);
nextTile = state.level.getTile(pos.x + 1f, pos.y);
if (nextTile == null) {
if (currDirection == Direction.LEFT) {
turned = true;
}
} else if (nextTile.isPassable() && Math.abs(y - ((int) y + 0.5f)) <= tolerance) {
y = ((int) y) + 0.5f;
} else if (nextTile.isPassable() && Math.abs(pos.y - ((int) pos.y + 0.5f)) <= tolerance) {
pos.y = ((int) pos.y) + 0.5f;
turned = true;
}
break;
case DOWN:
nextTile = state.level.getTile(x, y - 1f);
nextTile = state.level.getTile(pos.x, pos.y - 1f);
if (nextTile == null) {
if (currDirection == Direction.UP) {
turned = true;
}
} else if (nextTile.isPassable() && Math.abs(x - ((int) x + 0.5f)) <= tolerance) {
x = ((int) x) + 0.5f;
} else if (nextTile.isPassable() && Math.abs(pos.x - ((int) pos.x + 0.5f)) <= tolerance) {
pos.x = ((int) pos.x) + 0.5f;
turned = true;
}
break;
case LEFT:
nextTile = state.level.getTile(x - 1f, y);
nextTile = state.level.getTile(pos.x - 1f, pos.y);
if (nextTile == null) {
if (currDirection == Direction.RIGHT) {
turned = true;
}
} else if (nextTile.isPassable() && Math.abs(y - ((int) y + 0.5f)) <= tolerance) {
y = ((int) y) + 0.5f;
} else if (nextTile.isPassable() && Math.abs(pos.y - ((int) pos.y + 0.5f)) <= tolerance) {
pos.y = ((int) pos.y) + 0.5f;
turned = true;
}
break;
@ -87,50 +88,49 @@ public abstract class MovableEntity extends Entity {
float dist = speed * dt;
LevelTile currentTile = state.level.getTile(x, y);
LevelTile currentTile = state.level.getTile(pos.x, pos.y);
nextTile = null;
float new_y = y;
float new_x = x;
Vector2 new_pos = new Vector2(pos);
switch (currDirection) {
case UP:
if (currentTile == null && y > state.level.height + 1) {
new_y = -1f;
if (currentTile == null && pos.y > state.level.height + 1) {
new_pos.y = -1f;
canMove = true;
} else {
new_y += dist;
nextTile = state.level.getTile(new_x, new_y + 0.5f);
new_pos.y += dist;
nextTile = state.level.getTile(new_pos.x, new_pos.y + 0.5f);
canMove = nextTile == null || nextTile.isPassable();
}
break;
case RIGHT:
if (currentTile == null && x > state.level.width + 1) {
new_x = -1f;
if (currentTile == null && pos.x > state.level.width + 1) {
new_pos.x = -1f;
canMove = true;
} else {
new_x += dist;
nextTile = state.level.getTile(new_x + 0.5f, new_y);
new_pos.x += dist;
nextTile = state.level.getTile(new_pos.x + 0.5f, new_pos.y);
canMove = nextTile == null || nextTile.isPassable();
}
break;
case DOWN:
if (currentTile == null && y < 0) {
new_y = state.level.height + 1;
if (currentTile == null && pos.y < 0) {
new_pos.y = state.level.height + 1;
canMove = true;
} else {
new_y -= dist;
nextTile = state.level.getTile(new_x, new_y - 0.5f);
new_pos.y -= dist;
nextTile = state.level.getTile(new_pos.x, new_pos.y - 0.5f);
canMove = nextTile == null || nextTile.isPassable();
}
break;
case LEFT:
if (currentTile == null && x < 0) {
new_x = state.level.width + 1;
if (currentTile == null && pos.x < 0) {
new_pos.x = state.level.width + 1;
canMove = true;
} else {
new_x -= dist;
nextTile = state.level.getTile(new_x - 0.5f, new_y);
new_pos.x -= dist;
nextTile = state.level.getTile(new_pos.x - 0.5f, new_pos.y);
canMove = nextTile == null || nextTile.isPassable();
}
break;
@ -138,8 +138,8 @@ public abstract class MovableEntity extends Entity {
// if move isn't going to collide with wall, move normally.
// otherwise, trim would-be decimal and center entity on tile
x = canMove? new_x : ((int) new_x) + 0.5f;
y = canMove? new_y : ((int) new_y) + 0.5f;
pos.x = canMove? new_pos.x : ((int) new_pos.x) + 0.5f;
pos.y = canMove? new_pos.y : ((int) new_pos.y) + 0.5f;
}
public void setNextDirection(Direction direction) {
@ -148,4 +148,8 @@ public abstract class MovableEntity extends Entity {
}
}
public Direction getNextDirection() {
return nextDirection;
}
}

View File

@ -54,18 +54,18 @@ public class Pacman extends MovableEntity {
deathFrame++;
}
LevelTile tile = state.level.getTile(x, y);
LevelTile tile = state.level.getTile(pos.x, pos.y);
if (tile == null) {
return;
}
switch (tile) {
case PELLET:
state.eatPellet(x, y);
state.eatPellet(pos.x, pos.y);
freezeFrames = 1;
break;
case POWER_PELLET:
state.eatPowerPellet(x, y);
state.eatPowerPellet(pos.x, pos.y);
freezeFrames = 3;
break;
}

View File

@ -0,0 +1,16 @@
package com.me.pacman.entity.ai;
import com.me.pacman.state.PlayState;
public class BlinkyChaseBehaviour extends Behaviour {
public BlinkyChaseBehaviour(PlayState state) {
super(state);
}
@Override
public Target getTarget() {
return new Target(state.pacman.pos);
}
}

View File

@ -1,5 +1,7 @@
package com.me.pacman.entity.ai;
import com.badlogic.gdx.math.Vector2;
public class Target {
public int x;
@ -15,6 +17,17 @@ public class Target {
this.y = (int) y;
}
public Target(Vector2 vector) {
this(vector.x, vector.y);
}
public float distance_sqr(Vector2 vec) {
float d_x = x - vec.x;
float d_y = y - vec.y;
// a^2 + b^2 = c^2. thanks pythagoras!
return d_x * d_x + d_y * d_y;
}
public boolean targetReached(float x, float y) {
return this.x == (int) x && this.y == (int) y;
}

View File

@ -1,6 +1,7 @@
package com.me.pacman.level;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.PacDude;
public class Level {
@ -38,6 +39,10 @@ public class Level {
return getTile((int) x, (int) y);
}
public LevelTile getTile(Vector2 vec) {
return getTile(vec.x, vec.y);
}
public void setTile(int x, int y, LevelTile tile) {
tiles[y][x] = tile;
}

View File

@ -11,6 +11,7 @@ import com.me.pacman.PacDude;
import com.me.pacman.entity.Direction;
import com.me.pacman.entity.Ghost;
import com.me.pacman.entity.Pacman;
import com.me.pacman.entity.ai.BlinkyChaseBehaviour;
import com.me.pacman.level.Level;
import com.me.pacman.level.LevelTile;
@ -181,7 +182,10 @@ public class PlayState extends LevelState {
public void newRound() {
round++;
level = new Level(game,"level");
pacman = new Pacman(this, false);
ghosts[0] = new Ghost(this, 14, 19.5f, Direction.LEFT, 0, new BlinkyChaseBehaviour(this), null, null);
ghosts[0].currentBehaviour = ghosts[0].chaseBehaviour;
game.assets.siren.stop(sirenId);
@ -202,7 +206,8 @@ public class PlayState extends LevelState {
pacmanCaught = false;
pacman = new Pacman(this, false);
ghosts[0] = new Ghost(this, 14, 19.5f, Direction.LEFT, 0, null, null, null);
ghosts[0] = new Ghost(this, 14, 19.5f, Direction.LEFT, 0, new BlinkyChaseBehaviour(this), null, null);
ghosts[0].currentBehaviour = ghosts[0].chaseBehaviour;
}
public void startGame() {