Ghost movement working - first rev
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
package se.urmo.game.collision;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import se.urmo.game.map.GameMap;
|
||||
import se.urmo.game.util.Direction;
|
||||
|
||||
@ -7,6 +8,7 @@ import java.awt.Point;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class GhostCollisionChecker {
|
||||
private final GameMap map;
|
||||
|
||||
@ -32,16 +34,20 @@ public class GhostCollisionChecker {
|
||||
default -> Collections.EMPTY_LIST;
|
||||
};
|
||||
|
||||
System.out.println( direction + " bounderies for " + position + " are " + bounderies);
|
||||
log.debug("{} bounderies for {} are {}", direction, position, bounderies);
|
||||
|
||||
List<Point> normalizedBoundaries = bounderies.stream()
|
||||
.map(p -> normalizePosition(direction, p, agent_width, agent_height))
|
||||
.toList();
|
||||
|
||||
if (map.isSolid(normalizedBoundaries)) {
|
||||
if (! map.isSolid(normalizedBoundaries)) {
|
||||
log.debug("{} is open", direction);
|
||||
return normalizePosition(direction, position, agent_width, agent_height);
|
||||
}{
|
||||
log.debug("{} is blocked", direction);
|
||||
return null;
|
||||
}
|
||||
return null; // Blocked
|
||||
// Blocked
|
||||
}
|
||||
|
||||
public Point normalizePosition(Direction dir, Point pos, int agent_width, int agent_height) {
|
||||
@ -57,9 +63,9 @@ public class GhostCollisionChecker {
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
public List<Direction> isIntersection(Point position) {
|
||||
List<Direction> intersection = map.isIntersection(position);
|
||||
System.out.println("Possible travel directions: " + intersection);
|
||||
public List<Direction> calculateDirectionAlternatives(Point position) {
|
||||
List<Direction> intersection = map.directionAlternatives(position);
|
||||
log.info("Possible travel directions: {}", intersection);
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
|
||||
10
src/main/java/se/urmo/game/entities/BlinkyStrategy.java
Normal file
10
src/main/java/se/urmo/game/entities/BlinkyStrategy.java
Normal file
@ -0,0 +1,10 @@
|
||||
package se.urmo.game.entities;
|
||||
|
||||
import java.awt.Point;
|
||||
|
||||
public class BlinkyStrategy implements GhostStrategy {
|
||||
@Override
|
||||
public Point chooseTarget(PacMan pacman, Ghost self, Ghost blinky) {
|
||||
return pacman.getTilePosition();
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package se.urmo.game.entities;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import se.urmo.game.collision.GhostCollisionChecker;
|
||||
import se.urmo.game.main.Game;
|
||||
import se.urmo.game.map.GameMap;
|
||||
@ -13,6 +14,7 @@ import java.awt.Point;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class Ghost {
|
||||
private static final int COLLISION_BOX_SIZE = 16;
|
||||
private static final int GHOST_SPEED = 1;
|
||||
@ -25,19 +27,20 @@ public class Ghost {
|
||||
|
||||
private final Game game;
|
||||
private final GhostCollisionChecker collisionChecker;
|
||||
private final GhostStrategy strategy;
|
||||
private Point position;
|
||||
|
||||
private boolean moving = true;
|
||||
private int aniTick = 0;
|
||||
private int aniIndex = 0;
|
||||
private BufferedImage[] animation;
|
||||
private Direction direction = Direction.LEFT;
|
||||
private int movementTick = 0;
|
||||
|
||||
|
||||
public Ghost(Game game, GhostCollisionChecker collisionChecker, GhostStrategy strategy) {
|
||||
this.game = game;
|
||||
this.collisionChecker = collisionChecker;
|
||||
this.strategy = strategy;
|
||||
position = new Point(13 * 16 + 8 + GameMap.OFFSET_X, 4 * 16 + GameMap.OFFSET_Y);
|
||||
loadAnimation();
|
||||
}
|
||||
@ -64,36 +67,25 @@ public class Ghost {
|
||||
g.drawImage(COLLISION_BOX, position.x, position.y, COLLISION_BOX_SIZE, COLLISION_BOX_SIZE, null);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
public void update(PacMan pacman) {
|
||||
updateAnimationTick();
|
||||
updatePosition(pacman);
|
||||
}
|
||||
|
||||
private void updatePosition(PacMan pacman) {
|
||||
if(movementTick >= GHOST_MOVEMENT_UPDATE_FREQUENCY) {
|
||||
// if intersection - decide direction
|
||||
// else if direction isPassible
|
||||
List<Direction> i = collisionChecker.isIntersection(position);
|
||||
if(!i.isEmpty()){
|
||||
// Change direction
|
||||
if(i.contains(Direction.DOWN)) direction = Direction.DOWN;
|
||||
else if(i.contains(Direction.RIGHT)) direction = Direction.RIGHT;
|
||||
else if(i.contains(Direction.UP)) direction = Direction.UP;
|
||||
else direction = Direction.LEFT;
|
||||
log.info("Evaluating possible directions");
|
||||
Direction intendedDirection = chooseDirection(
|
||||
collisionChecker.calculateDirectionAlternatives(position),
|
||||
strategy.chooseTarget(pacman, null, null));
|
||||
|
||||
}
|
||||
|
||||
//Point target = strategy.chooseTarget(pacman, this, blinky);
|
||||
log.info("selecting direction {}", intendedDirection);
|
||||
Point newPosition = new Point(
|
||||
position.x += intendedDirection.dx * GHOST_SPEED,
|
||||
position.y += intendedDirection.dy * GHOST_SPEED);
|
||||
|
||||
Point newPosition = switch (direction){
|
||||
case RIGHT -> new Point(position.x += GHOST_SPEED, position.y);
|
||||
case LEFT -> new Point(position.x -= GHOST_SPEED, position.y);
|
||||
case DOWN -> new Point(position.x, position.y += GHOST_SPEED);
|
||||
case UP -> new Point(position.x, position.y -= GHOST_SPEED);
|
||||
default -> throw new IllegalStateException("Illegal direction");
|
||||
};
|
||||
|
||||
Point destination = collisionChecker.getValidDestination(direction, newPosition, GHOST_SIZE, GHOST_SIZE);
|
||||
|
||||
// if (position.x + direction.dx * GHOST_SPEED < GameMap.OFFSET_X) direction = direction.opposite();
|
||||
// if (position.x + GHOST_SIZE + (direction.dx * GHOST_SPEED) > GamePanel.SCREEN_WIDTH - GameMap.OFFSET_X)
|
||||
// direction = direction.opposite();
|
||||
Point destination = collisionChecker.getValidDestination(intendedDirection, newPosition, COLLISION_BOX_SIZE, COLLISION_BOX_SIZE);
|
||||
|
||||
if(destination != null) {
|
||||
position = destination;
|
||||
|
||||
7
src/main/java/se/urmo/game/entities/GhostStrategy.java
Normal file
7
src/main/java/se/urmo/game/entities/GhostStrategy.java
Normal file
@ -0,0 +1,7 @@
|
||||
package se.urmo.game.entities;
|
||||
|
||||
import java.awt.Point;
|
||||
|
||||
public interface GhostStrategy {
|
||||
Point chooseTarget(PacMan pacman, Ghost self, Ghost blinky);
|
||||
}
|
||||
@ -109,4 +109,8 @@ public class PacMan {
|
||||
public void setDirection(Direction direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public Point getTilePosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package se.urmo.game.map;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import se.urmo.game.main.GamePanel;
|
||||
import se.urmo.game.util.Direction;
|
||||
import se.urmo.game.util.LoadSave;
|
||||
@ -13,6 +14,7 @@ import java.util.Objects;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Slf4j
|
||||
public class GameMap {
|
||||
public static final int MAP_TILESIZE = 16;// 16px from left
|
||||
public static final int OFFSET_Y = 7 * MAP_TILESIZE; // 160px from top
|
||||
@ -169,22 +171,28 @@ public class GameMap {
|
||||
if(tile.getValue() == 0) tile.setImage(null);
|
||||
}
|
||||
|
||||
public List<Direction> isIntersection(Point position) {
|
||||
public List<Direction> directionAlternatives(Point position) {
|
||||
int row = (position.y - OFFSET_Y) / MAP_TILESIZE;
|
||||
int col = (position.x - OFFSET_X) / MAP_TILESIZE;
|
||||
|
||||
record DirectionCheck(int rowOffset, int colOffset, Direction direction) {}
|
||||
|
||||
log.debug("At [{}][{}]", row, col);
|
||||
return Stream.of(
|
||||
new DirectionCheck(0, 1, Direction.RIGHT),
|
||||
new DirectionCheck(0, -1, Direction.LEFT),
|
||||
new DirectionCheck(1, 0, Direction.DOWN),
|
||||
new DirectionCheck(-1, 0, Direction.UP)
|
||||
)
|
||||
.filter(dc -> !mapData[row + dc.rowOffset][col + dc.colOffset].isSolid())
|
||||
.filter(dc -> {
|
||||
int r = row + dc.rowOffset;
|
||||
int c = col + dc.colOffset;
|
||||
MapTile mapTile = mapData[r][c];
|
||||
boolean solid = mapTile.isSolid();
|
||||
log.debug("[{}][{}] {} is {} ({})", r, c, dc.direction, solid ? "solid" : " not solid", mapTile.getValue());
|
||||
return !solid;
|
||||
})
|
||||
.map(DirectionCheck::direction)
|
||||
.toList();
|
||||
|
||||
}
|
||||
|
||||
public boolean isSolid(List<Point> points) {
|
||||
@ -196,7 +204,7 @@ public class GameMap {
|
||||
int col = (x - OFFSET_X) / MAP_TILESIZE;
|
||||
MapTile mapTile = mapData[row][col];
|
||||
boolean solid = mapTile.isSolid();
|
||||
//System.out.println("["+row+"]["+col+"] is " + (solid?"solid":" not solid") + " (" + mapTile.getValue() + ")");
|
||||
log.debug("["+row+"]["+col+"] is " + (solid?"solid":" not solid") + " (" + mapTile.getValue() + ")");
|
||||
return solid;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
package se.urmo.game.map;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class MapTile {
|
||||
private final int value;
|
||||
private BufferedImage image;
|
||||
@ -11,14 +16,10 @@ public class MapTile {
|
||||
public MapTile(BufferedImage image, int value) {
|
||||
this.value = value;
|
||||
this.image = image;
|
||||
this.solid = value != 0;
|
||||
this.solid = value != 0 && value != 99;
|
||||
this.collisionMask = value != 0 ? createCollisionMask(image) : null;
|
||||
}
|
||||
|
||||
public boolean[][] getCollisionMask() {
|
||||
return collisionMask;
|
||||
}
|
||||
|
||||
private boolean[][] createCollisionMask(BufferedImage img) {
|
||||
if(img == null) return null;
|
||||
int w = img.getWidth();
|
||||
@ -33,19 +34,4 @@ public class MapTile {
|
||||
return mask;
|
||||
}
|
||||
|
||||
public BufferedImage getImage() {
|
||||
return this.image;
|
||||
}
|
||||
|
||||
public boolean isSolid() {
|
||||
return this.solid;
|
||||
}
|
||||
|
||||
public void setImage(BufferedImage img) {
|
||||
this.image = img;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package se.urmo.game.state;
|
||||
|
||||
import lombok.Getter;
|
||||
import se.urmo.game.collision.CollisionChecker;
|
||||
import se.urmo.game.collision.GhostCollisionChecker;
|
||||
import se.urmo.game.entities.BlinkyStrategy;
|
||||
import se.urmo.game.entities.Ghost;
|
||||
import se.urmo.game.entities.PacMan;
|
||||
import se.urmo.game.main.Game;
|
||||
@ -16,19 +18,20 @@ public class PlayingState implements GameState {
|
||||
private final GameStateManager gameStateManager;
|
||||
private final Ghost ghost;
|
||||
private PacMan pacman;
|
||||
@Getter
|
||||
private GameMap map;
|
||||
public PlayingState(Game game, GameStateManager gameStateManager) {
|
||||
this.game = game;
|
||||
this.gameStateManager = gameStateManager;
|
||||
this.map = new GameMap();
|
||||
this.pacman = new PacMan(game, new CollisionChecker(map));
|
||||
this.ghost = new Ghost(game, new GhostCollisionChecker(map), null);
|
||||
this.ghost = new Ghost(game, new GhostCollisionChecker(map), new BlinkyStrategy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
pacman.update();
|
||||
ghost.update();
|
||||
ghost.update(pacman);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -58,8 +61,4 @@ public class PlayingState implements GameState {
|
||||
pacman.setMoving(false);
|
||||
pacman.setDirection(Direction.NONE);
|
||||
}
|
||||
|
||||
public GameMap getMap() {
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user