Ghosts can chase!
Refactor position to be a Vector2 for convenience sake.
This commit is contained in:
parent
d10148ace1
commit
75d0c41bb5
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
16
core/src/com/me/pacman/entity/ai/BlinkyChaseBehaviour.java
Normal file
16
core/src/com/me/pacman/entity/ai/BlinkyChaseBehaviour.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user