Adding Ghost to SpriteLocation

This commit is contained in:
Urban Modig
2025-08-29 14:59:22 +02:00
parent f001cf0a41
commit 23fce8dded
8 changed files with 63 additions and 56 deletions

View File

@ -3,6 +3,8 @@ package se.urmo.game.entities;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import se.urmo.game.collision.GhostCollisionChecker;
import se.urmo.game.graphics.SpriteLocation;
import se.urmo.game.graphics.SpriteSheetManager;
import se.urmo.game.main.Game;
import se.urmo.game.map.GameMap;
import se.urmo.game.state.GhostManager;
@ -52,12 +54,13 @@ public class Ghost {
public Ghost(GhostCollisionChecker collisionChecker, GhostStrategy strategy, GhostStrategy scaterStrategy, BufferedImage[] animation, BufferedImage[] fearAnimation) {
public Ghost(GhostCollisionChecker collisionChecker, GhostStrategy strategy, GhostStrategy scaterStrategy, int animation) {
this.collisionChecker = collisionChecker;
this.chaseStrategy = strategy;
this.scaterStrategy = scaterStrategy;
this.baseAnimation = animation;
this.fearAnimation = fearAnimation;
this.baseAnimation = SpriteSheetManager.get(SpriteLocation.GHOST).getAnimation(animation);;
this.fearAnimation = SpriteSheetManager.get(SpriteLocation.GHOST).getAnimation(8);;
position = new Point(
13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X + (GameMap.MAP_TILESIZE / 2),
4 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + (GameMap.MAP_TILESIZE / 2) );

View File

@ -1,15 +1,18 @@
package se.urmo.game.map;
package se.urmo.game.graphics;
import lombok.Getter;
import se.urmo.game.entities.Ghost;
import se.urmo.game.map.GameMap;
import se.urmo.game.util.LoadSave;
import java.awt.image.BufferedImage;
@Getter
public enum SpriteLocation {
MAP("sprites/PacMan-custom-spritemap-0-3.png", 5, 11),
ITEM("sprites/PacManAssets-Items.png", 2, 8),
NONE("", 0, 0) { // Special case for tiles without sprites
MAP("sprites/PacMan-custom-spritemap-0-3.png", 5, 11, GameMap.MAP_TILESIZE),
ITEM("sprites/PacManAssets-Items.png", 2, 8, GameMap.MAP_TILESIZE),
GHOST("sprites/PacManAssets-Ghosts.png", 11, 4, Ghost.GHOST_SIZE),
NONE("", 0, 0, 0) { // Special case for tiles without sprites
@Override
public BufferedImage[][] loadSprites(int tileSize) {
return new BufferedImage[][] {{ null }}; // Single null sprite
@ -19,11 +22,13 @@ public enum SpriteLocation {
private final String path;
private final int rows;
private final int cols;
private final int size;
SpriteLocation(String path, int rows, int cols) {
SpriteLocation(String path, int rows, int cols, int size) {
this.path = path;
this.rows = rows;
this.cols = cols;
this.size = size;
}
public BufferedImage[][] loadSprites(int tileSize) {

View File

@ -1,6 +1,7 @@
package se.urmo.game.map;
package se.urmo.game.graphics;
import lombok.Getter;
import java.awt.image.BufferedImage;
@ -11,15 +12,7 @@ public class SpriteSheet {
public SpriteSheet(SpriteLocation location) {
this.location = location;
this.spriteSheet = location.loadSprites(GameMap.MAP_TILESIZE);
}
public BufferedImage getSprite(TileType tileType) {
if (tileType.getSpriteSheet() != location) {
throw new IllegalArgumentException("Tile type belongs to different sprite sheet");
}
// NONE will always return null without additional checks
return getSprite(tileType.getRow(), tileType.getCol());
this.spriteSheet = location.loadSprites(location.getSize());
}
public BufferedImage getSprite(int row, int col) {
@ -31,4 +24,12 @@ public class SpriteSheet {
}
return spriteSheet[row][col];
}
public BufferedImage[] getAnimation(int row) {
if(location == SpriteLocation.NONE) return null;
if(row>= spriteSheet.length) return null;
return spriteSheet[row];
}
}

View File

@ -1,4 +1,6 @@
package se.urmo.game.map;
package se.urmo.game.graphics;
import se.urmo.game.map.GameMap;
import java.util.EnumMap;
import java.util.Map;
@ -6,7 +8,7 @@ import java.util.Map;
public class SpriteSheetManager {
private static final Map<SpriteLocation, SpriteSheet> spriteSheets = new EnumMap<>(SpriteLocation.class);
public static SpriteSheet get(SpriteLocation location) {
public static SpriteSheet get(SpriteLocation location) {
return spriteSheets.computeIfAbsent(location, SpriteSheet::new);
}

View File

@ -1,6 +1,7 @@
package se.urmo.game.map;
import lombok.extern.slf4j.Slf4j;
import se.urmo.game.graphics.SpriteSheetManager;
import se.urmo.game.util.Direction;
import java.awt.*;
@ -31,28 +32,26 @@ public class GameMap {
BufferedReader br = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
String line;
int rowIndex = 0;
while ((line = br.readLine()) != null && rowIndex < mapRowSize) {
int row = 0;
while ((line = br.readLine()) != null && row < mapRowSize) {
String[] tokens = line.split(",");
if (tokens.length != mapColSize) {
throw new IllegalStateException(
"Invalid map format: row " + rowIndex + " has " + tokens.length +
"Invalid map format: row " + row + " has " + tokens.length +
" columns, expected " + mapColSize
);
}
for (int col = 0; col < mapColSize; col++) {
int value = Integer.parseInt(tokens[col].trim());
TileType type = TileType.fromValue(value);
BufferedImage sprite = SpriteSheetManager.get(type.getSpriteSheet()).getSprite(type);
data[rowIndex][col] = new MapTile(sprite, type);
data[row][col] = MapTile.byType(TileType.fromValue(value));
}
rowIndex++;
row++;
}
if (rowIndex != mapRowSize) {
if (row != mapRowSize) {
throw new IllegalStateException(
"Invalid map format: found " + rowIndex + " rows, expected " + mapRowSize
"Invalid map format: found " + row + " rows, expected " + mapRowSize
);
}
@ -78,10 +77,10 @@ public class GameMap {
}
}
private BufferedImage getSprite(int value) {
TileType type = TileType.fromValue(value);
return SpriteSheetManager.get(type.getSpriteSheet()).getSprite(type);
}
// private BufferedImage getSprite(int value) {
// TileType type = TileType.fromValue(value);
// return SpriteSheetManager.get(type.getSpriteSheet()).getSprite(type);
// }
public boolean isPassable(List<Point> list){
return list.stream().allMatch(p -> isPassable(p.x, p.y));

View File

@ -2,6 +2,7 @@ package se.urmo.game.map;
import lombok.Getter;
import lombok.Setter;
import se.urmo.game.graphics.SpriteSheetManager;
import java.awt.image.BufferedImage;
@ -12,12 +13,19 @@ public class MapTile {
private BufferedImage image;
private final boolean[][] collisionMask;
public MapTile(BufferedImage image, TileType tileType) {
this.tileType = tileType;
public MapTile(BufferedImage image, TileType type) {
this.tileType = type;
this.image = image;
this.collisionMask = tileType.isSolid() ? createCollisionMask(image) : null;
}
public static MapTile byType(TileType type) {
BufferedImage img = SpriteSheetManager
.get(type.getSpriteSheet())
.getSprite(type.getRow(), type.getCol()); // row and col in sprite - not in map!
return new MapTile(img, type) ;
}
private boolean[][] createCollisionMask(BufferedImage img) {
if(img == null) return null;
int w = img.getWidth();

View File

@ -1,6 +1,7 @@
package se.urmo.game.map;
import lombok.Getter;
import se.urmo.game.graphics.SpriteLocation;
@Getter
public enum TileType {

View File

@ -26,10 +26,12 @@ import java.util.List;
public class GhostManager {
public static final int SPRITE_SHEET_ROWS = 10;
public static final int MAX_SPRITE_FRAMES = 4;
public static final int BLINKY_ANIMATION = 0;
public static final int PINKY_ANIMATION = 2;
public static final int INKY_ANIMATION = 1;
public static final int CLYDE_ANIMATION = 3;
@Getter
private final List<Ghost> ghosts = new ArrayList<>();
private BufferedImage[][] image;
private GhostMode globalMode;
private long lastModeSwitchTime;
private int phaseIndex = 0;
@ -43,31 +45,17 @@ public class GhostManager {
};
public GhostManager(GhostCollisionChecker ghostCollisionChecker) {
loadAnimation();
// Create ghosts with their strategies
Ghost blinky = new Ghost(ghostCollisionChecker, new BlinkyStrategy(), new ScatterToTopRight(), image[0], image[9]);
Ghost blinky = new Ghost(ghostCollisionChecker, new BlinkyStrategy(), new ScatterToTopRight(), BLINKY_ANIMATION);
ghosts.add(blinky);
//ghosts.add(new Ghost(ghostCollisionChecker, new PinkyStrategy(),new ScatterToTopLeft(), image[2], image[9]));
//ghosts.add(new Ghost(ghostCollisionChecker,new InkyStrategy(blinky), new ScatterToBottomRight(), image[1], image[9]));
//ghosts.add(new Ghost(ghostCollisionChecker, new ClydeStrategy(), new ScatterToBottomLeft(), image[3], image[8]));
ghosts.add(new Ghost(ghostCollisionChecker, new PinkyStrategy(), new ScatterToTopLeft(), PINKY_ANIMATION));
ghosts.add(new Ghost(ghostCollisionChecker,new InkyStrategy(blinky), new ScatterToBottomRight(), INKY_ANIMATION));
ghosts.add(new Ghost(ghostCollisionChecker, new ClydeStrategy(), new ScatterToBottomLeft(), CLYDE_ANIMATION));
setMode(GhostMode.CHASE);
}
private void loadAnimation() {
image = new BufferedImage[SPRITE_SHEET_ROWS][MAX_SPRITE_FRAMES];
BufferedImage img = LoadSave.GetSpriteAtlas("sprites/PacManAssets-Ghosts.png");
for (int row = 0; row < SPRITE_SHEET_ROWS; row++) {
for (int col = 0; col < MAX_SPRITE_FRAMES; col++) {
image[row][col] = img.getSubimage(Ghost.GHOST_SIZE * col, Ghost.GHOST_SIZE * row, Ghost.GHOST_SIZE, Ghost.GHOST_SIZE);
}
}
}
public void setMode(GhostMode mode) {
this.globalMode = mode;
log.debug("Mode changed to {}", globalMode);
log.debug("Mode changed to {}", mode);
for (Ghost g : ghosts) {
g.setMode(mode);
}