intersection works

This commit is contained in:
Urban Modig
2025-08-17 10:53:59 +02:00
parent acab63d91c
commit 1bbba216d2
6 changed files with 151 additions and 12 deletions

View File

@ -1,8 +1,65 @@
package se.urmo.game.collision; package se.urmo.game.collision;
import se.urmo.game.map.GameMap; 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 { public class GhostCollisionChecker {
private final GameMap map;
public GhostCollisionChecker(GameMap map) { public GhostCollisionChecker(GameMap map) {
this.map = map;
}
public Point getValidDestination(Direction direction, Point position, int agent_width, int agent_height) {
List<Point> 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<Point> 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<Direction> isIntersection(Point position) {
List<Direction> intersection = map.isIntersection(position);
System.out.println("Possible travel directions: " + intersection);
return intersection;
} }
} }

View File

@ -2,7 +2,6 @@ package se.urmo.game.entities;
import se.urmo.game.collision.GhostCollisionChecker; import se.urmo.game.collision.GhostCollisionChecker;
import se.urmo.game.main.Game; import se.urmo.game.main.Game;
import se.urmo.game.main.GamePanel;
import se.urmo.game.map.GameMap; import se.urmo.game.map.GameMap;
import se.urmo.game.util.Direction; import se.urmo.game.util.Direction;
import se.urmo.game.util.LoadSave; import se.urmo.game.util.LoadSave;
@ -12,6 +11,7 @@ import java.awt.Color;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Point; import java.awt.Point;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.List;
public class Ghost { public class Ghost {
private static final int COLLISION_BOX_SIZE = 16; 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 static final BufferedImage COLLISION_BOX = MiscUtil.createOutlinedBox(COLLISION_BOX_SIZE, COLLISION_BOX_SIZE, Color.black, 2);
private final Game game; private final Game game;
private final GhostCollisionChecker ghostCollisionChecker; private final GhostCollisionChecker collisionChecker;
private final Point position; private Point position;
private boolean moving = true; private boolean moving = true;
private int aniTick = 0; private int aniTick = 0;
@ -35,9 +35,9 @@ public class Ghost {
private int movementTick = 0; private int movementTick = 0;
public Ghost(Game game, GhostCollisionChecker ghostCollisionChecker) { public Ghost(Game game, GhostCollisionChecker collisionChecker, GhostStrategy strategy) {
this.game = game; this.game = game;
this.ghostCollisionChecker = ghostCollisionChecker; this.collisionChecker = collisionChecker;
position = new Point(13 * 16 + 8 + GameMap.OFFSET_X, 4 * 16 + GameMap.OFFSET_Y); position = new Point(13 * 16 + 8 + GameMap.OFFSET_X, 4 * 16 + GameMap.OFFSET_Y);
loadAnimation(); loadAnimation();
} }
@ -67,16 +67,57 @@ public class Ghost {
public void update() { public void update() {
updateAnimationTick(); updateAnimationTick();
if(movementTick >= GHOST_MOVEMENT_UPDATE_FREQUENCY) { 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;
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; movementTick = 0;
} else movementTick++; } else movementTick++;
} }
private Direction chooseDirection(List<Direction> 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() { private void updateAnimationTick() {
if (moving) { if (moving) {
aniTick++; aniTick++;

View File

@ -79,4 +79,8 @@ public class Game implements Runnable {
public GamePanel getGamePanel() { public GamePanel getGamePanel() {
return gamePanel; return gamePanel;
} }
public GameStateManager getGameStateManager() {
return gameStateManager;
}
} }

View File

@ -1,6 +1,7 @@
package se.urmo.game.map; package se.urmo.game.map;
import se.urmo.game.main.GamePanel; import se.urmo.game.main.GamePanel;
import se.urmo.game.util.Direction;
import se.urmo.game.util.LoadSave; import se.urmo.game.util.LoadSave;
import java.awt.*; import java.awt.*;
@ -10,6 +11,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.Stream;
public class GameMap { public class GameMap {
public static final int MAP_TILESIZE = 16;// 16px from left public static final int MAP_TILESIZE = 16;// 16px from left
@ -166,4 +168,35 @@ public class GameMap {
MapTile tile = mapData[row][col]; MapTile tile = mapData[row][col];
if(tile.getValue() == 0) tile.setImage(null); if(tile.getValue() == 0) tile.setImage(null);
} }
public List<Direction> 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<Point> 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;
}
} }

View File

@ -37,8 +37,8 @@ public class MapTile {
return this.image; return this.image;
} }
public boolean isPassable() { public boolean isSolid() {
return ! this.solid; return this.solid;
} }
public void setImage(BufferedImage img) { public void setImage(BufferedImage img) {

View File

@ -22,7 +22,7 @@ public class PlayingState implements GameState {
this.gameStateManager = gameStateManager; this.gameStateManager = gameStateManager;
this.map = new GameMap(); this.map = new GameMap();
this.pacman = new PacMan(game, new CollisionChecker(map)); 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 @Override
@ -58,4 +58,8 @@ public class PlayingState implements GameState {
pacman.setMoving(false); pacman.setMoving(false);
pacman.setDirection(Direction.NONE); pacman.setDirection(Direction.NONE);
} }
public GameMap getMap() {
return map;
}
} }