diff --git a/src/main/java/se/urmo/game/collision/CollisionChecker.java b/src/main/java/se/urmo/game/collision/CollisionChecker.java index 194e1a8..4095a79 100644 --- a/src/main/java/se/urmo/game/collision/CollisionChecker.java +++ b/src/main/java/se/urmo/game/collision/CollisionChecker.java @@ -2,13 +2,11 @@ package se.urmo.game.collision; import lombok.extern.slf4j.Slf4j; -import se.urmo.game.entities.MyPoint; +import se.urmo.game.util.MyPoint; import se.urmo.game.main.GamePanel; import se.urmo.game.util.Direction; import se.urmo.game.map.GameMap; -import se.urmo.game.util.Pair; -import java.awt.Point; import java.util.Collections; import java.util.List; diff --git a/src/main/java/se/urmo/game/collision/GhostCollisionChecker.java b/src/main/java/se/urmo/game/collision/GhostCollisionChecker.java index f64323a..88d5593 100644 --- a/src/main/java/se/urmo/game/collision/GhostCollisionChecker.java +++ b/src/main/java/se/urmo/game/collision/GhostCollisionChecker.java @@ -1,14 +1,11 @@ package se.urmo.game.collision; import lombok.extern.slf4j.Slf4j; -import se.urmo.game.entities.MyPoint; +import se.urmo.game.util.MyPoint; import se.urmo.game.map.GameMap; -import se.urmo.game.map.MapTile; import se.urmo.game.util.Direction; -import java.awt.Point; import java.util.List; -import java.util.stream.Stream; @Slf4j public class GhostCollisionChecker { diff --git a/src/main/java/se/urmo/game/entities/Animated.java b/src/main/java/se/urmo/game/entities/Animated.java new file mode 100644 index 0000000..2f2aa9c --- /dev/null +++ b/src/main/java/se/urmo/game/entities/Animated.java @@ -0,0 +1,8 @@ +package se.urmo.game.entities; + +public interface Animated { + void updateAnimationTick(); + int getAnimationSpeed(); + boolean isPaused(); + +} diff --git a/src/main/java/se/urmo/game/entities/BaseAnimated.java b/src/main/java/se/urmo/game/entities/BaseAnimated.java new file mode 100644 index 0000000..ac0434e --- /dev/null +++ b/src/main/java/se/urmo/game/entities/BaseAnimated.java @@ -0,0 +1,41 @@ +package se.urmo.game.entities; + +import lombok.Setter; + +abstract public class BaseAnimated implements Animated { + private final int animationSpeed; + private final int maxSpriteFrames; + @Setter + protected boolean paused = true; + private int aniTick = 0; + protected int aniIndex = 0; + + protected BaseAnimated(int animationSpeed, int maxSpriteFrames) { + this.animationSpeed = animationSpeed; + this.maxSpriteFrames = maxSpriteFrames; + } + + + public void updateAnimationTick() { + if (!paused) { + aniTick++; + if (aniTick >= animationSpeed) { + aniTick = 0; + aniIndex++; + if (aniIndex >= maxSpriteFrames) { + aniIndex = 0; + } + } + } + } + + @Override + public int getAnimationSpeed() { + return animationSpeed; + } + + @Override + public boolean isPaused() { + return paused; + } +} diff --git a/src/main/java/se/urmo/game/entities/Fruit.java b/src/main/java/se/urmo/game/entities/collectibles/Fruit.java similarity index 92% rename from src/main/java/se/urmo/game/entities/Fruit.java rename to src/main/java/se/urmo/game/entities/collectibles/Fruit.java index 5f91f89..bbbbdf4 100644 --- a/src/main/java/se/urmo/game/entities/Fruit.java +++ b/src/main/java/se/urmo/game/entities/collectibles/Fruit.java @@ -1,7 +1,8 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.collectibles; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import se.urmo.game.entities.pacman.PacMan; import se.urmo.game.map.GameMap; import se.urmo.game.state.FruitType; @@ -35,8 +36,6 @@ public class Fruit { } public boolean collidesWith(PacMan pacman) { - //return pacman.distanceTo(position) < GameMap.MAP_TILESIZE / 2.0; - Rectangle pacmanBounds = pacman.getBounds(); Rectangle fruitBounds = new Rectangle(position.x, position.y, sprite.getWidth(), sprite.getHeight()); return pacmanBounds.intersects(fruitBounds); diff --git a/src/main/java/se/urmo/game/entities/Ghost.java b/src/main/java/se/urmo/game/entities/ghost/Ghost.java similarity index 69% rename from src/main/java/se/urmo/game/entities/Ghost.java rename to src/main/java/se/urmo/game/entities/ghost/Ghost.java index 2d7173a..f912ec8 100644 --- a/src/main/java/se/urmo/game/entities/Ghost.java +++ b/src/main/java/se/urmo/game/entities/ghost/Ghost.java @@ -1,8 +1,11 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; import se.urmo.game.collision.GhostCollisionChecker; +import se.urmo.game.entities.BaseAnimated; +import se.urmo.game.entities.ghost.strategy.FearStrategy; +import se.urmo.game.entities.ghost.strategy.GhostStrategy; +import se.urmo.game.entities.pacman.PacMan; import se.urmo.game.graphics.SpriteLocation; import se.urmo.game.graphics.SpriteSheetManager; import se.urmo.game.main.Game; @@ -10,6 +13,7 @@ import se.urmo.game.map.GameMap; import se.urmo.game.state.GhostManager; import se.urmo.game.util.Direction; import se.urmo.game.util.MiscUtil; +import se.urmo.game.util.MyPoint; import java.awt.Color; import java.awt.Graphics; @@ -20,7 +24,7 @@ import java.util.Map; import java.util.stream.Collectors; @Slf4j -public class Ghost { +public class Ghost extends BaseAnimated { private static final double BASE_SPEED = 0.40; private static final int WARNING_THRESHOLD = 180; // 3 seconds of warning private static final int COLLISION_BOX_SIZE = 16; @@ -35,17 +39,11 @@ public class Ghost { private final MyPoint startPos; private final BufferedImage[] fearAnimation; private final BufferedImage[] baseAnimation; - //@Getter - //private Point position; - private MyPoint position2; + private MyPoint position; - private boolean moving = true; - private int aniTick = 0; - private int aniIndex = 0; private final GhostStrategy scaterStrategy; private GhostStrategy currentStrategy; private BufferedImage[] animation; - private int movementTick = 0; private Direction direction; private Direction prevDirection; private GhostMode mode; @@ -55,16 +53,17 @@ public class Ghost { public Ghost(GhostCollisionChecker collisionChecker, GhostStrategy strategy, GhostStrategy scaterStrategy, int animation) { + super(ANIMATION_UPDATE_FREQUENCY, GhostManager.MAX_SPRITE_FRAMES); this.collisionChecker = collisionChecker; this.chaseStrategy = strategy; this.scaterStrategy = scaterStrategy; this.baseAnimation = SpriteSheetManager.get(SpriteLocation.GHOST).getAnimation(animation); this.fearAnimation = SpriteSheetManager.get(SpriteLocation.GHOST).getAnimation(8); - position2 = new MyPoint( + position = new MyPoint( 13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X + ((double) GameMap.MAP_TILESIZE / 2), 4 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + ((double) GameMap.MAP_TILESIZE / 2)); - startPos = position2; + startPos = position; this.currentStrategy = chaseStrategy; this.animation = baseAnimation; } @@ -72,19 +71,14 @@ public class Ghost { public void draw(Graphics g) { g.drawImage( animation[aniIndex], - (int) (position2.x - GHOST_SIZE / 2), - (int) (position2.y - GHOST_SIZE / 2), + (int) position.x - GHOST_SIZE / 2, + (int) position.y - GHOST_SIZE / 2, GHOST_SIZE, GHOST_SIZE, null); - g.drawImage(COLLISION_BOX, - (int) (position2.x - COLLISION_BOX_OFFSET), - (int) (position2.y - COLLISION_BOX_OFFSET), - COLLISION_BOX_SIZE, - COLLISION_BOX_SIZE, null); } public void update(PacMan pacman, GameMap map) { - updateAnimationTick(); + //updateAnimationTick(); if (mode == GhostMode.FRIGHTENED) { updateInFrightendMode(); } @@ -103,19 +97,16 @@ public class Ghost { } private void updatePosition(PacMan pacman, GameMap map) { - if (map.isAligned(new Point((int) position2.x, (int) position2.y))) { + if (map.isAligned(new Point((int) position.x, (int) position.y))) { log.info("Evaluating possible directions"); prevDirection = direction; direction = chooseDirection( - prioritize(collisionChecker.calculateDirectionAlternatives(position2)), + prioritize(collisionChecker.calculateDirectionAlternatives(position)), currentStrategy.chooseTarget(this, pacman, map)); log.info("selecting direction {}", direction); } moveTo(getNewPosition()); - - movementTick = 0; - } /** @@ -126,8 +117,8 @@ public class Ghost { */ private MyPoint getNewPosition() { return new MyPoint( - position2.x + direction.dx * getSpeed(), - position2.y + direction.dy * getSpeed()); + position.x + direction.dx * getSpeed(), + position.y + direction.dy * getSpeed()); } @@ -137,12 +128,19 @@ public class Ghost { private void moveTo(MyPoint newPosition) { MyPoint destination = collisionChecker.canMoveTo(direction, newPosition.x, newPosition.y); -log.info("Moving to {} from {}. Destination: {}", direction, position2, newPosition); if (destination != null) { - position2 = destination; + position = destination; } } + /** + * Creates a map of directions and their associated priority values based on the given list of directions. + * Directions with a value of {@code Direction.NONE} are excluded. If a direction is opposite to the + * previous direction, it is given a higher priority value. + * + * @param directions a list of potential movement directions + * @return a map where keys are valid directions and values are their associated priority + */ private Map prioritize(List directions) { return directions.stream() .filter(d -> d != Direction.NONE) @@ -153,6 +151,16 @@ log.info("Moving to {} from {}. Destination: {}", direction, position2, newPosit } + /** + * Selects the best movement direction for the ghost based on priority and distance to the target. + * The method evaluates the directions with the lowest priority value and chooses the one that + * minimizes the distance to the target point. + * + * @param options a map where keys represent potential movement directions and values + * represent their associated priority levels + * @param target the target point towards which the direction is evaluated + * @return the direction that has the lowest priority and minimizes the distance to the target + */ private Direction chooseDirection(Map options, Point target) { // Find the lowest priority int lowestPriority = options.values().stream() @@ -160,18 +168,19 @@ log.info("Moving to {} from {}. Destination: {}", direction, position2, newPosit .min() .orElse(Integer.MAX_VALUE); - // Return all directions that have this priority + // Collect all directions that have this priority List directions = options.entrySet().stream() .filter(entry -> entry.getValue() == lowestPriority) .map(Map.Entry::getKey) .toList(); + // Calculate the direction that has the lowest distance to the target Direction best = directions.getFirst(); double bestDist = Double.MAX_VALUE; for (Direction d : directions) { - int nx = (int) position2.x + d.dx * GameMap.MAP_TILESIZE; - int ny = (int) position2.y + d.dy * GameMap.MAP_TILESIZE; + double nx = position.x + d.dx * GameMap.MAP_TILESIZE; + double ny = position.y + d.dy * GameMap.MAP_TILESIZE; double dist = target.distance(nx, ny); if (dist < bestDist) { bestDist = dist; @@ -181,19 +190,19 @@ log.info("Moving to {} from {}. Destination: {}", direction, position2, newPosit return best; } - private void updateAnimationTick() { - if (moving) { - aniTick++; - if (aniTick >= ANIMATION_UPDATE_FREQUENCY) { - aniTick = 0; - aniIndex++; - if (aniIndex >= GhostManager.MAX_SPRITE_FRAMES) { - aniIndex = 0; - } - - } - } - } +// private void updateAnimationTick() { +// if (moving) { +// aniTick++; +// if (aniTick >= ANIMATION_UPDATE_FREQUENCY) { +// aniTick = 0; +// aniIndex++; +// if (aniIndex >= GhostManager.MAX_SPRITE_FRAMES) { +// aniIndex = 0; +// } +// +// } +// } +// } public void setMode(GhostMode mode) { this.mode = mode; @@ -218,10 +227,10 @@ log.info("Moving to {} from {}. Destination: {}", direction, position2, newPosit } public void resetPosition() { - position2 = startPos; + position = startPos; } public Point getPosition() { - return new Point((int) position2.x, (int) position2.y); + return new Point((int) position.x, (int) position.y); } } diff --git a/src/main/java/se/urmo/game/entities/GhostMode.java b/src/main/java/se/urmo/game/entities/ghost/GhostMode.java similarity index 67% rename from src/main/java/se/urmo/game/entities/GhostMode.java rename to src/main/java/se/urmo/game/entities/ghost/GhostMode.java index a144cbf..64dabb0 100644 --- a/src/main/java/se/urmo/game/entities/GhostMode.java +++ b/src/main/java/se/urmo/game/entities/ghost/GhostMode.java @@ -1,4 +1,4 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost; public enum GhostMode { CHASE, diff --git a/src/main/java/se/urmo/game/entities/BlinkyStrategy.java b/src/main/java/se/urmo/game/entities/ghost/strategy/BlinkyStrategy.java similarity index 65% rename from src/main/java/se/urmo/game/entities/BlinkyStrategy.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/BlinkyStrategy.java index 09d9e9b..2dbcbf4 100644 --- a/src/main/java/se/urmo/game/entities/BlinkyStrategy.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/BlinkyStrategy.java @@ -1,5 +1,7 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import java.awt.Point; diff --git a/src/main/java/se/urmo/game/entities/ClydeStrategy.java b/src/main/java/se/urmo/game/entities/ghost/strategy/ClydeStrategy.java similarity index 81% rename from src/main/java/se/urmo/game/entities/ClydeStrategy.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/ClydeStrategy.java index 2aaec5d..36fdb34 100644 --- a/src/main/java/se/urmo/game/entities/ClydeStrategy.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/ClydeStrategy.java @@ -1,5 +1,7 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import java.awt.Point; diff --git a/src/main/java/se/urmo/game/entities/FearStrategy.java b/src/main/java/se/urmo/game/entities/ghost/strategy/FearStrategy.java similarity index 89% rename from src/main/java/se/urmo/game/entities/FearStrategy.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/FearStrategy.java index 2b9ee77..1318ade 100644 --- a/src/main/java/se/urmo/game/entities/FearStrategy.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/FearStrategy.java @@ -1,5 +1,7 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import se.urmo.game.util.Direction; diff --git a/src/main/java/se/urmo/game/entities/GhostStrategy.java b/src/main/java/se/urmo/game/entities/ghost/strategy/GhostStrategy.java similarity index 54% rename from src/main/java/se/urmo/game/entities/GhostStrategy.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/GhostStrategy.java index 53e26e8..4cb99c4 100644 --- a/src/main/java/se/urmo/game/entities/GhostStrategy.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/GhostStrategy.java @@ -1,5 +1,7 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import java.awt.Point; diff --git a/src/main/java/se/urmo/game/entities/InkyStrategy.java b/src/main/java/se/urmo/game/entities/ghost/strategy/InkyStrategy.java similarity index 91% rename from src/main/java/se/urmo/game/entities/InkyStrategy.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/InkyStrategy.java index 93f2c9e..7991b16 100644 --- a/src/main/java/se/urmo/game/entities/InkyStrategy.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/InkyStrategy.java @@ -1,5 +1,7 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import se.urmo.game.util.Direction; diff --git a/src/main/java/se/urmo/game/entities/PinkyStrategy.java b/src/main/java/se/urmo/game/entities/ghost/strategy/PinkyStrategy.java similarity index 79% rename from src/main/java/se/urmo/game/entities/PinkyStrategy.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/PinkyStrategy.java index a2770dc..e07286a 100644 --- a/src/main/java/se/urmo/game/entities/PinkyStrategy.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/PinkyStrategy.java @@ -1,11 +1,13 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import se.urmo.game.util.Direction; import java.awt.Point; -public class PinkyStrategy implements GhostStrategy{ +public class PinkyStrategy implements GhostStrategy { @Override public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { Direction pacmanDir = pacman.getDirection(); diff --git a/src/main/java/se/urmo/game/entities/ScatterToBottomLeft.java b/src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToBottomLeft.java similarity index 70% rename from src/main/java/se/urmo/game/entities/ScatterToBottomLeft.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToBottomLeft.java index b041a45..ca81e88 100644 --- a/src/main/java/se/urmo/game/entities/ScatterToBottomLeft.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToBottomLeft.java @@ -1,5 +1,7 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import java.awt.Point; diff --git a/src/main/java/se/urmo/game/entities/ScatterToBottomRight.java b/src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToBottomRight.java similarity index 72% rename from src/main/java/se/urmo/game/entities/ScatterToBottomRight.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToBottomRight.java index 40810e0..6a8582b 100644 --- a/src/main/java/se/urmo/game/entities/ScatterToBottomRight.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToBottomRight.java @@ -1,5 +1,7 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import java.awt.Point; diff --git a/src/main/java/se/urmo/game/entities/ScatterToTopLeft.java b/src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToTopLeft.java similarity index 67% rename from src/main/java/se/urmo/game/entities/ScatterToTopLeft.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToTopLeft.java index 5866dc5..57bcbb2 100644 --- a/src/main/java/se/urmo/game/entities/ScatterToTopLeft.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToTopLeft.java @@ -1,5 +1,7 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import java.awt.Point; diff --git a/src/main/java/se/urmo/game/entities/ScatterToTopRight.java b/src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToTopRight.java similarity index 56% rename from src/main/java/se/urmo/game/entities/ScatterToTopRight.java rename to src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToTopRight.java index 264335c..4000012 100644 --- a/src/main/java/se/urmo/game/entities/ScatterToTopRight.java +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/ScatterToTopRight.java @@ -1,10 +1,12 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.ghost.strategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import java.awt.Point; -public class ScatterToTopRight implements GhostStrategy{ +public class ScatterToTopRight implements GhostStrategy { @Override public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { return new Point((map.columns() -1) * GameMap.MAP_TILESIZE + GameMap.OFFSET_X, 0); diff --git a/src/main/java/se/urmo/game/entities/PacMan.java b/src/main/java/se/urmo/game/entities/pacman/PacMan.java similarity index 63% rename from src/main/java/se/urmo/game/entities/PacMan.java rename to src/main/java/se/urmo/game/entities/pacman/PacMan.java index 63b3909..826b9da 100644 --- a/src/main/java/se/urmo/game/entities/PacMan.java +++ b/src/main/java/se/urmo/game/entities/pacman/PacMan.java @@ -1,14 +1,14 @@ -package se.urmo.game.entities; +package se.urmo.game.entities.pacman; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import se.urmo.game.collision.CollisionChecker; +import se.urmo.game.entities.BaseAnimated; import se.urmo.game.util.Direction; -import se.urmo.game.main.Game; import se.urmo.game.map.GameMap; import se.urmo.game.util.LoadSave; -import se.urmo.game.util.MiscUtil; +import se.urmo.game.util.MyPoint; import java.awt.*; import java.awt.image.BufferedImage; @@ -16,40 +16,35 @@ import java.util.Arrays; @Slf4j -public class PacMan { +public class PacMan extends BaseAnimated { 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 Game game; - //private final Point startPosition; private final MyPoint startPosition; - private int aniTick = 0; - private int aniIndex = 0; private static final int ANIMATION_UPDATE_FREQUENCY = 10; private static final double BASE_SPEED = 0.40; - @Setter - private boolean moving; - private final BufferedImage[][] movmentImages = new BufferedImage[4][4]; + private boolean moving = false; + private final BufferedImage[][] spriteSheets; private MyPoint position; - private static final BufferedImage COLLISION_BOX = MiscUtil.createOutlinedBox(COLLISION_BOX_SIZE, COLLISION_BOX_SIZE, Color.yellow, 2); private final CollisionChecker collisionChecker; @Setter @Getter private Direction direction = Direction.NONE; - public PacMan(Game game, CollisionChecker collisionChecker) { - this.game = game; + public PacMan(CollisionChecker collisionChecker) { + super(ANIMATION_UPDATE_FREQUENCY, 4); this.collisionChecker = collisionChecker; this.position = new MyPoint( 26 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X, 13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + ((double) GameMap.MAP_TILESIZE / 2)); this.startPosition = this.position; - loadAnimation(); + this.spriteSheets = loadAnimation(); } - private void loadAnimation() { + private BufferedImage[][] loadAnimation() { BufferedImage[][] image = new BufferedImage[3][4]; + BufferedImage[][] spriteMap = new BufferedImage[4][4];; BufferedImage img = LoadSave.GetSpriteAtlas("sprites/PacManAssets-PacMan.png"); for (int row = 0; row < 3; row++) { @@ -57,34 +52,30 @@ public class PacMan { image[row][col] = img.getSubimage(PACMAN_SIZE * col, PACMAN_SIZE * row, PACMAN_SIZE, PACMAN_SIZE); } } - movmentImages[Direction.RIGHT.ordinal()] = image[0]; - movmentImages[Direction.LEFT.ordinal()] = Arrays.stream(image[0]) + spriteMap[Direction.RIGHT.ordinal()] = image[0]; + spriteMap[Direction.LEFT.ordinal()] = Arrays.stream(image[0]) .map(i -> LoadSave.rotate(i, 180)) .toArray(BufferedImage[]::new); - movmentImages[Direction.DOWN.ordinal()] = Arrays.stream(image[0]) + spriteMap[Direction.DOWN.ordinal()] = Arrays.stream(image[0]) .map(i -> LoadSave.rotate(i, 90)) .toArray(BufferedImage[]::new); - movmentImages[Direction.UP.ordinal()] = Arrays.stream(image[0]) + spriteMap[Direction.UP.ordinal()] = Arrays.stream(image[0]) .map(i -> LoadSave.rotate(i, 270)) .toArray(BufferedImage[]::new); + return spriteMap; } public void draw(Graphics g) { g.drawImage( - movmentImages[direction==Direction.NONE ? 0 : direction.ordinal()][aniIndex], + spriteSheets[direction==Direction.NONE ? 0 : direction.ordinal()][aniIndex], (int) position.x - PACMAN_OFFSET, (int) position.y - PACMAN_OFFSET, PACMAN_SIZE, PACMAN_SIZE, null); - //g.drawImage(COLLISION_BOX, position.x - COLLISION_BOX_OFFSET, position.y - COLLISION_BOX_OFFSET, COLLISION_BOX_SIZE, COLLISION_BOX_SIZE, null); - //g.setColor(Color.BLUE); - //g.fillRect(position.x-1, position.y-1, 3, 3); } public void update() { - updateAnimationTick(); if(moving) { - log.debug("Moving to {}", direction); MyPoint mpoint = switch (direction) { case RIGHT -> new MyPoint(position.x + getSpeed(), position.y); case LEFT -> new MyPoint(position.x - getSpeed(), position.y); @@ -105,41 +96,32 @@ public class PacMan { return BASE_SPEED * 0.8; } - private void updateAnimationTick() { - if (moving) { - aniTick++; - if (aniTick >= ANIMATION_UPDATE_FREQUENCY) { - aniTick = 0; - aniIndex++; - if (aniIndex >= 4) { - aniIndex = 0; - } - - } - } - } - public double distanceTo(Point point) { return new Point((int) position.x, (int) position.y).distance(point); } - public void loseLife() { - - } - public void resetPosition() { position = startPosition; } public Image getLifeIcon() { - return movmentImages[0][1]; + return spriteSheets[0][1]; } public Rectangle getBounds() { - return new Rectangle((int) (position.x - COLLISION_BOX_OFFSET), (int) (position.y - COLLISION_BOX_OFFSET), COLLISION_BOX_SIZE, COLLISION_BOX_SIZE); + return new Rectangle( + (int) (position.x - COLLISION_BOX_OFFSET), + (int) (position.y - COLLISION_BOX_OFFSET), + COLLISION_BOX_SIZE, + COLLISION_BOX_SIZE); } public Point getPosition() { return new Point((int) position.x, (int) position.y); } + + public void setMoving(boolean b) { + moving = b; + paused = !b; + } } diff --git a/src/main/java/se/urmo/game/graphics/SpriteLocation.java b/src/main/java/se/urmo/game/graphics/SpriteLocation.java index ee8213f..1323363 100644 --- a/src/main/java/se/urmo/game/graphics/SpriteLocation.java +++ b/src/main/java/se/urmo/game/graphics/SpriteLocation.java @@ -1,7 +1,7 @@ package se.urmo.game.graphics; import lombok.Getter; -import se.urmo.game.entities.Ghost; +import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.map.GameMap; import se.urmo.game.util.LoadSave; diff --git a/src/main/java/se/urmo/game/state/AnimationManager.java b/src/main/java/se/urmo/game/state/AnimationManager.java new file mode 100644 index 0000000..0e4b744 --- /dev/null +++ b/src/main/java/se/urmo/game/state/AnimationManager.java @@ -0,0 +1,23 @@ +package se.urmo.game.state; + +import se.urmo.game.entities.Animated; + +import java.util.ArrayList; +import java.util.List; + +public class AnimationManager { + private final List animatedEntities = new ArrayList<>(); + + public void register(Animated animated) { + animatedEntities.add(animated); + } + + public void unregister(Animated entity) { + animatedEntities.remove(entity); + } + + public void updateAll() { + animatedEntities.forEach(Animated::updateAnimationTick); + } + +} diff --git a/src/main/java/se/urmo/game/state/FruitManager.java b/src/main/java/se/urmo/game/state/FruitManager.java index 77543fc..d88b8bf 100644 --- a/src/main/java/se/urmo/game/state/FruitManager.java +++ b/src/main/java/se/urmo/game/state/FruitManager.java @@ -1,8 +1,8 @@ package se.urmo.game.state; import lombok.extern.slf4j.Slf4j; -import se.urmo.game.entities.Fruit; -import se.urmo.game.entities.PacMan; +import se.urmo.game.entities.collectibles.Fruit; +import se.urmo.game.entities.pacman.PacMan; import java.awt.Graphics; diff --git a/src/main/java/se/urmo/game/state/GhostManager.java b/src/main/java/se/urmo/game/state/GhostManager.java index e1fa671..4fa9574 100644 --- a/src/main/java/se/urmo/game/state/GhostManager.java +++ b/src/main/java/se/urmo/game/state/GhostManager.java @@ -3,22 +3,20 @@ package se.urmo.game.state; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import se.urmo.game.collision.GhostCollisionChecker; -import se.urmo.game.entities.BlinkyStrategy; -import se.urmo.game.entities.ClydeStrategy; -import se.urmo.game.entities.Ghost; -import se.urmo.game.entities.GhostMode; -import se.urmo.game.entities.InkyStrategy; -import se.urmo.game.entities.PacMan; -import se.urmo.game.entities.PinkyStrategy; -import se.urmo.game.entities.ScatterToBottomLeft; -import se.urmo.game.entities.ScatterToBottomRight; -import se.urmo.game.entities.ScatterToTopLeft; -import se.urmo.game.entities.ScatterToTopRight; +import se.urmo.game.entities.ghost.strategy.BlinkyStrategy; +import se.urmo.game.entities.ghost.strategy.ClydeStrategy; +import se.urmo.game.entities.ghost.Ghost; +import se.urmo.game.entities.ghost.GhostMode; +import se.urmo.game.entities.ghost.strategy.InkyStrategy; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.entities.ghost.strategy.PinkyStrategy; +import se.urmo.game.entities.ghost.strategy.ScatterToBottomLeft; +import se.urmo.game.entities.ghost.strategy.ScatterToBottomRight; +import se.urmo.game.entities.ghost.strategy.ScatterToTopLeft; +import se.urmo.game.entities.ghost.strategy.ScatterToTopRight; import se.urmo.game.map.GameMap; -import se.urmo.game.util.LoadSave; import java.awt.Graphics2D; -import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; @@ -44,13 +42,15 @@ public class GhostManager { 5000, Integer.MAX_VALUE // scatter 5s, then chase forever }; - public GhostManager(GhostCollisionChecker ghostCollisionChecker) { + public GhostManager(GhostCollisionChecker ghostCollisionChecker, AnimationManager animationManager) { // Create ghosts with their strategies Ghost blinky = new Ghost(ghostCollisionChecker, new BlinkyStrategy(), new ScatterToTopRight(), BLINKY_ANIMATION); ghosts.add(blinky); - // 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)); + 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)); + + ghosts.forEach(animationManager::register); setMode(GhostMode.CHASE); } diff --git a/src/main/java/se/urmo/game/state/PlayingState.java b/src/main/java/se/urmo/game/state/PlayingState.java index 29a9b95..a2f4ce2 100644 --- a/src/main/java/se/urmo/game/state/PlayingState.java +++ b/src/main/java/se/urmo/game/state/PlayingState.java @@ -4,9 +4,9 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import se.urmo.game.collision.CollisionChecker; import se.urmo.game.collision.GhostCollisionChecker; -import se.urmo.game.entities.Ghost; -import se.urmo.game.entities.GhostMode; -import se.urmo.game.entities.PacMan; +import se.urmo.game.entities.ghost.Ghost; +import se.urmo.game.entities.ghost.GhostMode; +import se.urmo.game.entities.pacman.PacMan; import se.urmo.game.main.Game; import se.urmo.game.map.GameMap; import se.urmo.game.map.MapTile; @@ -25,6 +25,7 @@ public class PlayingState implements GameState { private final Font arcadeFont; private final FruitManager fruitManager; private final LevelManager levelManager; + private final AnimationManager animationManager; private PacMan pacman; @Getter private GameMap map; @@ -35,8 +36,10 @@ public class PlayingState implements GameState { this.game = game; this.gameStateManager = gameStateManager; this.map = new GameMap("maps/map1.csv"); - this.pacman = new PacMan(game, new CollisionChecker(map)); - this.ghostManager = new GhostManager(new GhostCollisionChecker(map)); + this.animationManager = new AnimationManager(); + this.pacman = new PacMan(new CollisionChecker(map)); + animationManager.register(pacman); + this.ghostManager = new GhostManager(new GhostCollisionChecker(map), animationManager); this.levelManager = new LevelManager(); this.fruitManager = new FruitManager(levelManager); this.arcadeFont = loadArcadeFont(); @@ -44,6 +47,7 @@ public class PlayingState implements GameState { @Override public void update() { + animationManager.updateAll(); pacman.update(); ghostManager.update(pacman, map); fruitManager.update(pacman, this); diff --git a/src/main/java/se/urmo/game/entities/MyPoint.java b/src/main/java/se/urmo/game/util/MyPoint.java similarity index 95% rename from src/main/java/se/urmo/game/entities/MyPoint.java rename to src/main/java/se/urmo/game/util/MyPoint.java index af2eb19..87dc677 100644 --- a/src/main/java/se/urmo/game/entities/MyPoint.java +++ b/src/main/java/se/urmo/game/util/MyPoint.java @@ -1,4 +1,4 @@ -package se.urmo.game.entities; +package se.urmo.game.util; public class MyPoint { public final double x;