From 1bbba216d2fe238480a7885c8b8d8acbc6b82198 Mon Sep 17 00:00:00 2001 From: Urban Modig Date: Sun, 17 Aug 2025 10:53:59 +0200 Subject: [PATCH] intersection works --- .../game/collision/GhostCollisionChecker.java | 57 ++++++++++++++++++ .../java/se/urmo/game/entities/Ghost.java | 59 ++++++++++++++++--- src/main/java/se/urmo/game/main/Game.java | 4 ++ src/main/java/se/urmo/game/map/GameMap.java | 33 +++++++++++ src/main/java/se/urmo/game/map/MapTile.java | 4 +- .../java/se/urmo/game/state/PlayingState.java | 6 +- 6 files changed, 151 insertions(+), 12 deletions(-) diff --git a/src/main/java/se/urmo/game/collision/GhostCollisionChecker.java b/src/main/java/se/urmo/game/collision/GhostCollisionChecker.java index 2da9b1d..e17b530 100644 --- a/src/main/java/se/urmo/game/collision/GhostCollisionChecker.java +++ b/src/main/java/se/urmo/game/collision/GhostCollisionChecker.java @@ -1,8 +1,65 @@ package se.urmo.game.collision; import se.urmo.game.map.GameMap; +import se.urmo.game.util.Direction; + +import java.awt.Point; +import java.util.Collections; +import java.util.List; public class GhostCollisionChecker { + private final GameMap map; + public GhostCollisionChecker(GameMap map) { + this.map = map; + } + + public Point getValidDestination(Direction direction, Point position, int agent_width, int agent_height) { + + List bounderies = switch (direction) { + case RIGHT -> List.of( + new Point(position.x + agent_width, position.y), // TOPRIGHT + new Point(position.x + agent_width, position.y + agent_height)); // BOTTOMRIGHT + case LEFT -> List.of( + position, // TOPLEFT + new Point(position.x, position.y + agent_height)); // BOTTOMLEFT + case UP -> List.of( + position, // TOPLEFT + new Point(position.x + agent_width, position.y)); // TOPRIGHT + case DOWN -> List.of( + new Point(position.x, position.y + agent_height), // BOTTOMLEFT + new Point(position.x + agent_width, position.y + agent_height)); // BOTTOMRIGHT + default -> Collections.EMPTY_LIST; + }; + + System.out.println( direction + " bounderies for " + position + " are " + bounderies); + + List normalizedBoundaries = bounderies.stream() + .map(p -> normalizePosition(direction, p, agent_width, agent_height)) + .toList(); + + if (map.isSolid(normalizedBoundaries)) { + return normalizePosition(direction, position, agent_width, agent_height); + } + return null; // Blocked + } + + public Point normalizePosition(Direction dir, Point pos, int agent_width, int agent_height) { + int x = pos.x; + int y = pos.y; + int width = map.getWidth(); + int height = map.getHeight(); + + // tunnel + if (x < GameMap.OFFSET_X) x = width - agent_width - GameMap.OFFSET_X; // right + if (x >= (width - GameMap.OFFSET_X)) x = GameMap.OFFSET_X; // left + + return new Point(x, y); + } + + public List isIntersection(Point position) { + List intersection = map.isIntersection(position); + System.out.println("Possible travel directions: " + intersection); + return intersection; } } diff --git a/src/main/java/se/urmo/game/entities/Ghost.java b/src/main/java/se/urmo/game/entities/Ghost.java index d999771..bfcd150 100644 --- a/src/main/java/se/urmo/game/entities/Ghost.java +++ b/src/main/java/se/urmo/game/entities/Ghost.java @@ -2,7 +2,6 @@ package se.urmo.game.entities; import se.urmo.game.collision.GhostCollisionChecker; import se.urmo.game.main.Game; -import se.urmo.game.main.GamePanel; import se.urmo.game.map.GameMap; import se.urmo.game.util.Direction; import se.urmo.game.util.LoadSave; @@ -12,6 +11,7 @@ import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.awt.image.BufferedImage; +import java.util.List; public class Ghost { private static final int COLLISION_BOX_SIZE = 16; @@ -24,8 +24,8 @@ public class Ghost { private static final BufferedImage COLLISION_BOX = MiscUtil.createOutlinedBox(COLLISION_BOX_SIZE, COLLISION_BOX_SIZE, Color.black, 2); private final Game game; - private final GhostCollisionChecker ghostCollisionChecker; - private final Point position; + private final GhostCollisionChecker collisionChecker; + private Point position; private boolean moving = true; private int aniTick = 0; @@ -35,9 +35,9 @@ public class Ghost { private int movementTick = 0; - public Ghost(Game game, GhostCollisionChecker ghostCollisionChecker) { + public Ghost(Game game, GhostCollisionChecker collisionChecker, GhostStrategy strategy) { this.game = game; - this.ghostCollisionChecker = ghostCollisionChecker; + this.collisionChecker = collisionChecker; position = new Point(13 * 16 + 8 + GameMap.OFFSET_X, 4 * 16 + GameMap.OFFSET_Y); loadAnimation(); } @@ -67,16 +67,57 @@ public class Ghost { public void update() { updateAnimationTick(); if(movementTick >= GHOST_MOVEMENT_UPDATE_FREQUENCY) { + // if intersection - decide direction + // else if direction isPassible + List 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; - 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(); + } - position.x += direction.dx * GHOST_SPEED; + //Point target = strategy.chooseTarget(pacman, this, blinky); + + 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(); + + if(destination != null) { + position = destination; + } movementTick = 0; } else movementTick++; } + private Direction chooseDirection(List options, Point target) { + Direction best = options.get(0); + double bestDist = Double.MAX_VALUE; + + for (Direction d : options) { + int nx = position.x + d.dx * GameMap.MAP_TILESIZE; + int ny = position.y + d.dy * GameMap.MAP_TILESIZE; + double dist = target.distance(nx, ny); + if (dist < bestDist) { + bestDist = dist; + best = d; + } + } + return best; + } + private void updateAnimationTick() { if (moving) { aniTick++; diff --git a/src/main/java/se/urmo/game/main/Game.java b/src/main/java/se/urmo/game/main/Game.java index 1279eb5..27993d3 100644 --- a/src/main/java/se/urmo/game/main/Game.java +++ b/src/main/java/se/urmo/game/main/Game.java @@ -79,4 +79,8 @@ public class Game implements Runnable { public GamePanel getGamePanel() { return gamePanel; } + + public GameStateManager getGameStateManager() { + return gameStateManager; + } } diff --git a/src/main/java/se/urmo/game/map/GameMap.java b/src/main/java/se/urmo/game/map/GameMap.java index ddbfe3f..9bdf913 100644 --- a/src/main/java/se/urmo/game/map/GameMap.java +++ b/src/main/java/se/urmo/game/map/GameMap.java @@ -1,6 +1,7 @@ package se.urmo.game.map; import se.urmo.game.main.GamePanel; +import se.urmo.game.util.Direction; import se.urmo.game.util.LoadSave; import java.awt.*; @@ -10,6 +11,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.IntStream; +import java.util.stream.Stream; public class GameMap { public static final int MAP_TILESIZE = 16;// 16px from left @@ -166,4 +168,35 @@ public class GameMap { MapTile tile = mapData[row][col]; if(tile.getValue() == 0) tile.setImage(null); } + + public List isIntersection(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) {} + + 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()) + .map(DirectionCheck::direction) + .toList(); + + } + + public boolean isSolid(List points) { + return points.stream().allMatch(p -> isSolid(p.x, p.y)); + } + + private boolean isSolid(int x, int y) { + int row = (y - OFFSET_Y) / MAP_TILESIZE; + 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() + ")"); + return solid; + } } diff --git a/src/main/java/se/urmo/game/map/MapTile.java b/src/main/java/se/urmo/game/map/MapTile.java index e640c02..402359f 100644 --- a/src/main/java/se/urmo/game/map/MapTile.java +++ b/src/main/java/se/urmo/game/map/MapTile.java @@ -37,8 +37,8 @@ public class MapTile { return this.image; } - public boolean isPassable() { - return ! this.solid; + public boolean isSolid() { + return this.solid; } public void setImage(BufferedImage img) { diff --git a/src/main/java/se/urmo/game/state/PlayingState.java b/src/main/java/se/urmo/game/state/PlayingState.java index 265534a..f7dcc43 100644 --- a/src/main/java/se/urmo/game/state/PlayingState.java +++ b/src/main/java/se/urmo/game/state/PlayingState.java @@ -22,7 +22,7 @@ public class PlayingState implements GameState { this.gameStateManager = gameStateManager; this.map = new GameMap(); this.pacman = new PacMan(game, new CollisionChecker(map)); - this.ghost = new Ghost(game, new GhostCollisionChecker(map)); + this.ghost = new Ghost(game, new GhostCollisionChecker(map), null); } @Override @@ -58,4 +58,8 @@ public class PlayingState implements GameState { pacman.setMoving(false); pacman.setDirection(Direction.NONE); } + + public GameMap getMap() { + return map; + } }