Refactor PacMan to utilize SpriteSheetManager.

Replaced hardcoded sprite loading logic with centralized SpriteSheetManager to improve maintainability and reduce duplication. Adjusted related methods to use the new Sprites record for streamlined sprite management.
This commit is contained in:
Urban Modig
2025-09-02 21:47:50 +02:00
parent 5ba16402e4
commit 328098bbe7
2 changed files with 37 additions and 39 deletions

View File

@ -5,13 +5,18 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import se.urmo.game.collision.CollisionChecker;
import se.urmo.game.entities.BaseAnimated;
import se.urmo.game.graphics.SpriteLocation;
import se.urmo.game.graphics.SpriteSheetManager;
import se.urmo.game.map.GameMap;
import se.urmo.game.state.LevelManager;
import se.urmo.game.util.Direction;
import se.urmo.game.map.GameMap;
import se.urmo.game.util.LoadSave;
import se.urmo.game.util.MyPoint;
import java.awt.*;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.stream.Stream;
@ -19,32 +24,25 @@ import java.util.stream.Stream;
@Slf4j
public class PacMan extends BaseAnimated {
private enum PacmanState {
ALIVE, DYING, DEAD
}
public static final int PACMAN_SIZE = 32;
public static final int PACMAN_OFFSET = PACMAN_SIZE / 2;
private static final int COLLISION_BOX_SIZE = 16;
private static final int COLLISION_BOX_OFFSET = (PACMAN_SIZE - COLLISION_BOX_SIZE) / 2;
private final MyPoint startPosition;
private static final int ANIMATION_UPDATE_FREQUENCY = 10;
private static final double BASE_SPEED = 0.40;
private boolean moving = false;
private final BufferedImage[][] spriteSheets;// [row][col]
private MyPoint position;
private static final long FRAME_NS = 80_000_000L; // 80 ms
private final MyPoint startPosition;
private final CollisionChecker collisionChecker;
private final LevelManager levelManager;
private final Sprites sprites;
private boolean moving = false;
private MyPoint position;
@Setter
@Getter
private Direction direction = Direction.NONE;
private double pacmanLevelSpeed;
private static BufferedImage[] deathFramesBase; // original, flattened [0..7]
private final double pacmanLevelSpeed;
private BufferedImage[] deathFrames; // working copy
private long lastChangeNs;
private static final long FRAME_NS = 80_000_000L; // 80 ms
// animation state
@Setter
private PacmanState state = PacmanState.ALIVE;
@ -58,40 +56,31 @@ public class PacMan extends BaseAnimated {
26 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X,
13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + ((double) GameMap.MAP_TILESIZE / 2));
this.startPosition = this.position;
Sprites spriteSheets1 = loadAnimation();
this.spriteSheets = spriteSheets1.spriteSheets;
this.deathFramesBase = spriteSheets1.deathFrames;
this.pacmanLevelSpeed = this.levelManager.getPacmanLevelSpeed();
}
this.sprites = loadAnimation();
this.pacmanLevelSpeed = this.levelManager.getPacmanLevelSpeed();
record Sprites(BufferedImage[][] spriteSheets, BufferedImage[] deathFrames) {
}
private Sprites loadAnimation() {
BufferedImage[][] image = new BufferedImage[3][4];
BufferedImage[][] spriteMap = new BufferedImage[6][4];
BufferedImage[] deathFrames;
BufferedImage img = LoadSave.GetSpriteAtlas("sprites/PacManAssets-PacMan.png");
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 4; col++) {
image[row][col] = img.getSubimage(PACMAN_SIZE * col, PACMAN_SIZE * row, PACMAN_SIZE, PACMAN_SIZE);
}
}
spriteMap[Direction.RIGHT.ordinal()] = image[0];
spriteMap[Direction.LEFT.ordinal()] = Arrays.stream(image[0])
BufferedImage[] animation = SpriteSheetManager.get(SpriteLocation.PACMAN).getAnimation(0);
spriteMap[Direction.RIGHT.ordinal()] = animation;
spriteMap[Direction.LEFT.ordinal()] = Arrays.stream(animation)
.map(i -> LoadSave.rotate(i, Direction.LEFT.angel))
.toArray(BufferedImage[]::new);
spriteMap[Direction.DOWN.ordinal()] = Arrays.stream(image[0])
spriteMap[Direction.DOWN.ordinal()] = Arrays.stream(animation)
.map(i -> LoadSave.rotate(i, 90))
.toArray(BufferedImage[]::new);
spriteMap[Direction.UP.ordinal()] = Arrays.stream(image[0])
spriteMap[Direction.UP.ordinal()] = Arrays.stream(animation)
.map(i -> LoadSave.rotate(i, 270))
.toArray(BufferedImage[]::new);
deathFrames = Stream.concat(Arrays.stream(image[1]), Arrays.stream(image[2]))
deathFrames = Stream.concat(
Arrays.stream(SpriteSheetManager.get(SpriteLocation.PACMAN).getAnimation(1)),
Arrays.stream(SpriteSheetManager.get(SpriteLocation.PACMAN).getAnimation(2)))
.toArray(BufferedImage[]::new);
return new Sprites(spriteMap, deathFrames);
}
@ -105,7 +94,7 @@ public class PacMan extends BaseAnimated {
private void drawAlive(Graphics g) {
if (state != PacmanState.ALIVE) return; // ignore if not dying/dead
g.drawImage(
spriteSheets[direction == Direction.NONE ? 0 : direction.ordinal()][aniIndex],
sprites.spriteSheets[direction == Direction.NONE ? 0 : direction.ordinal()][aniIndex],
(int) position.x - PACMAN_OFFSET,
(int) position.y - PACMAN_OFFSET,
PACMAN_SIZE,
@ -136,7 +125,7 @@ public class PacMan extends BaseAnimated {
if (state != PacmanState.DYING) return;
long now = System.nanoTime();
while (now - lastChangeNs >= FRAME_NS && deathFrameIdx < deathFramesBase.length - 1) {
while (now - lastChangeNs >= FRAME_NS && deathFrameIdx < sprites.deathFrames.length - 1) {
deathFrameIdx++;
lastChangeNs += FRAME_NS; // carry over exact cadence
}
@ -168,7 +157,7 @@ public class PacMan extends BaseAnimated {
state = PacmanState.DYING;
deathFrameIdx = 0;
lastChangeNs = System.nanoTime(); // reset stopwatch right now
deathFrames = Arrays.stream(deathFramesBase)
deathFrames = Arrays.stream(sprites.deathFrames)
.map(img -> LoadSave.rotate(img, direction.angel))
.toArray(BufferedImage[]::new);
}
@ -197,7 +186,7 @@ public class PacMan extends BaseAnimated {
}
public Image getLifeIcon() {
return spriteSheets[0][1];
return sprites.spriteSheets[0][1];
}
public Rectangle getBounds() {
@ -225,4 +214,11 @@ public class PacMan extends BaseAnimated {
// PACMAN_SIZE,
// PACMAN_SIZE, null);
}
private enum PacmanState {
ALIVE, DYING, DEAD
}
record Sprites(BufferedImage[][] spriteSheets, BufferedImage[] deathFrames) {
}
}

View File

@ -2,6 +2,7 @@ package se.urmo.game.graphics;
import lombok.Getter;
import se.urmo.game.entities.ghost.Ghost;
import se.urmo.game.entities.pacman.PacMan;
import se.urmo.game.map.GameMap;
import se.urmo.game.util.LoadSave;
@ -12,6 +13,7 @@ public enum SpriteLocation {
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),
PACMAN("sprites/PacManAssets-PacMan.png", 3, 4, PacMan.PACMAN_SIZE),
NONE("", 0, 0, 0) { // Special case for tiles without sprites
@Override
public BufferedImage[][] loadSprites(int tileSize) {