diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/se/urmo/game/CollisionChecker.java b/src/main/java/se/urmo/game/CollisionChecker.java
new file mode 100644
index 0000000..b3a8ab4
--- /dev/null
+++ b/src/main/java/se/urmo/game/CollisionChecker.java
@@ -0,0 +1,57 @@
+package se.urmo.game;
+
+
+import java.awt.Point;
+import java.util.Collections;
+import java.util.List;
+
+public class CollisionChecker {
+ private GameMap map;
+
+ public CollisionChecker(GameMap map) {
+ this.map = map;
+ }
+
+ public Point getValidDestination(Direction direction, Point position, int agent_width, int agent_height) {
+ List list = 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;
+ };
+
+ List list2 = list.stream()
+ .map(p -> normalizePosition(direction, p, agent_width, agent_height))
+ .toList();
+
+ if (map.isPassable(list2)) {
+ 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
+//
+// if (y < 0) y = height - 1; // optional vertical wrap
+// else if (y >= height) y = 0;
+
+ return new Point(x, y);
+ }
+}
diff --git a/src/main/java/se/urmo/game/Direction.java b/src/main/java/se/urmo/game/Direction.java
new file mode 100644
index 0000000..7911bbc
--- /dev/null
+++ b/src/main/java/se/urmo/game/Direction.java
@@ -0,0 +1,21 @@
+package se.urmo.game;
+
+public enum Direction {
+ // private static final int RIGHT = 0;
+// private static final int LEFT = 1;
+// private static final int DOWN = 2;
+// private static final int UP = 3;
+ RIGHT(1, 0),
+ LEFT(-1, 0),
+ DOWN(0, 1),
+ UP(0, -1),
+ NONE(0, 0);
+
+ public final int dx;
+ public final int dy;
+
+ Direction(int dx, int dy) {
+ this.dx = dx;
+ this.dy = dy;
+ }
+}
diff --git a/src/main/java/se/urmo/game/Game.java b/src/main/java/se/urmo/game/Game.java
index 8dd8723..f0490bb 100644
--- a/src/main/java/se/urmo/game/Game.java
+++ b/src/main/java/se/urmo/game/Game.java
@@ -1,9 +1,8 @@
package se.urmo.game;
-import se.urmo.game.state.Playing;
+import se.urmo.game.state.GameStateManager;
import javax.swing.*;
-import java.awt.*;
public class Game implements Runnable {
public final static int FPS_SET = 120;
@@ -12,15 +11,20 @@ public class Game implements Runnable {
private final static double timePerUpdate = 1000000000.0 / UPS_SET;
+ private final GameStateManager gameStateManager;
private Thread gameThread;
private final JFrame window = new JFrame();
- private final GamePanel gamePanel = new GamePanel(this);
- private Playing playing = new Playing(this);
+ private final GamePanel gamePanel;
+
+ public Game() {
+ this.gameStateManager = new GameStateManager(this);
+ this.gamePanel = new GamePanel(this, gameStateManager);
+ }
public void start() {
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
- window.setTitle("2d");
+ window.setTitle("PacMan");
window.add(gamePanel);
@@ -30,14 +34,9 @@ public class Game implements Runnable {
window.setLocationRelativeTo(null);
window.setVisible(true);
- startGameThread();
- }
-
- public void startGameThread() {
this.gameThread = new Thread(this);
gameThread.start();
}
-
@Override
public void run() {
long previousTime = System.nanoTime();
@@ -57,7 +56,7 @@ public class Game implements Runnable {
previousTime = currentTime;
if (deltaU >= 1) {
- update();
+ gameStateManager.update();
updates++;
deltaU--;
}
@@ -77,18 +76,7 @@ public class Game implements Runnable {
}
}
- private void update() {
- playing.update();
- }
-
- public Playing getPlaying() {
- return playing;
- }
public GamePanel getGamePanel() {
return gamePanel;
}
-
- public void render(Graphics g) {
- playing.draw(g);
- }
}
diff --git a/src/main/java/se/urmo/game/GameMap.java b/src/main/java/se/urmo/game/GameMap.java
index 5289d3d..7eba51f 100644
--- a/src/main/java/se/urmo/game/GameMap.java
+++ b/src/main/java/se/urmo/game/GameMap.java
@@ -73,17 +73,24 @@ public class GameMap {
}
}
+ public boolean isPassable(List list) {
+ return list.stream().allMatch(p -> isPassable(p.x, p.y));
+ }
+
public boolean isPassable(int x, int y) {
int row = (y - OFFSET_Y) / MAP_TILESIZE;
int col = (x - OFFSET_X) / MAP_TILESIZE;
-
- if (row > mapData.length - 1) row = 0;
- if (col > mapData[row].length - 1) col = 0;
- if (row < 0) row = mapData.length - 1;
- if (col < 0) col = mapData[row].length - 1;
-
+ System.out.println("(x,y)" + x+","+y + " -> col,row: " + col + "," +row);
boolean passable = mapData[row][col].isPassable();
System.out.println(row + "," + col + "is" +(passable?"":" not") + " passable");
return passable;
}
+
+ public int getWidth() {
+ return GamePanel.SCREEN_WIDTH;
+ }
+
+ public int getHeight() {
+ return GamePanel.SCREEN_HEIGHT;
+ }
}
diff --git a/src/main/java/se/urmo/game/GamePanel.java b/src/main/java/se/urmo/game/GamePanel.java
index 205f10a..7eab92f 100644
--- a/src/main/java/se/urmo/game/GamePanel.java
+++ b/src/main/java/se/urmo/game/GamePanel.java
@@ -1,10 +1,10 @@
package se.urmo.game;
import se.urmo.game.input.KeyHandler;
+import se.urmo.game.state.GameStateManager;
import javax.swing.*;
import java.awt.*;
-import java.awt.event.KeyEvent;
public class GamePanel extends JPanel {
public static final int ORIGINAL_TILE_SIZE = 8;
@@ -15,16 +15,15 @@ public class GamePanel extends JPanel {
public static final int SCREEN_WIDTH = MAX_SCREEN_COL * TILE_SIZE;
public static final int SCREEN_HEIGHT = MAX_SCREEN_ROW * TILE_SIZE;
private final Game game;
+ private final GameStateManager gameStateManager;
-
- KeyHandler keyHandler = new KeyHandler(this);
-
- public GamePanel(Game game) {
+ public GamePanel(Game game, GameStateManager gameStateManager) {
this.game = game;
+ this.gameStateManager = gameStateManager;
this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
this.setBackground(Color.decode("#473B78"));
this.setDoubleBuffered(true);
- this.addKeyListener(keyHandler);
+ this.addKeyListener(new KeyHandler(gameStateManager));
this.setFocusable(true);
}
@@ -41,15 +40,6 @@ public class GamePanel extends JPanel {
int border = 16;
g2d.setColor(new Color(20, 20, 28)); // Dark bluish-black like in the image
g2d.fillRect(border, border, getWidth() - 2 * border, getHeight() - 2 * border);
- game.render(g);
- }
-
-
- public void keyPressed(KeyEvent e) {
- game.getPlaying().keyPressed(e);
- }
-
- public void keyReleased(KeyEvent e) {
- game.getPlaying().keyReleased();
+ gameStateManager.render((Graphics2D) g);
}
}
diff --git a/src/main/java/se/urmo/game/PacMan.java b/src/main/java/se/urmo/game/PacMan.java
index a51b9f1..b2a2fb6 100644
--- a/src/main/java/se/urmo/game/PacMan.java
+++ b/src/main/java/se/urmo/game/PacMan.java
@@ -1,6 +1,5 @@
package se.urmo.game;
-import se.urmo.game.state.CollisionChecker;
import se.urmo.game.util.LoadSave;
import se.urmo.game.util.MiscUtil;
@@ -10,10 +9,6 @@ import java.util.Arrays;
public class PacMan {
- private static final int RIGHT = 0;
- private static final int LEFT = 1;
- private static final int DOWN = 2;
- private static final int UP = 3;
public static final int PACMAN_SIZE = GamePanel.TILE_SIZE;
private final Game game;
private int aniTick = 0;
@@ -22,14 +17,16 @@ public class PacMan {
private int speed = 1;
private boolean moving;
private final BufferedImage[][] movmentImages = new BufferedImage[4][4];
- private int direction = 0;
- private int xPos = 14*16 + 8, yPos = 29*16; // top left of object
- private static final BufferedImage innerBox = MiscUtil.createOutlinedBox(8,8, Color.yellow, 2);
+ //private int xPos = 14 * 16 + 8, yPos = 29 * 16; // top left of object
+ private Point position;
+ private static final BufferedImage innerBox = MiscUtil.createOutlinedBox(8, 8, Color.yellow, 2);
private CollisionChecker collisionChecker;
+ private Direction direction = Direction.NONE;
public PacMan(Game game, CollisionChecker collisionChecker) {
this.game = game;
this.collisionChecker = collisionChecker;
+ position = new Point(26 * 16 + 8 + GameMap.OFFSET_X, 13 * 16 + GameMap.OFFSET_Y);
loadAnimation();
}
@@ -42,91 +39,46 @@ public class PacMan {
image[row][col] = img.getSubimage(32 * col, 32 * row, PACMAN_SIZE, PACMAN_SIZE);
}
}
- movmentImages[RIGHT] = image[0];
- movmentImages[LEFT] = Arrays.stream(image[0])
+ movmentImages[Direction.RIGHT.ordinal()] = image[0];
+ movmentImages[Direction.LEFT.ordinal()] = Arrays.stream(image[0])
.map(i -> LoadSave.rotate(i, 180))
.toArray(BufferedImage[]::new);
- movmentImages[DOWN] = Arrays.stream(image[0])
+ movmentImages[Direction.DOWN.ordinal()] = Arrays.stream(image[0])
.map(i -> LoadSave.rotate(i, 90))
.toArray(BufferedImage[]::new);
- movmentImages[UP] = Arrays.stream(image[0])
+ movmentImages[Direction.UP.ordinal()] = Arrays.stream(image[0])
.map(i -> LoadSave.rotate(i, 270))
.toArray(BufferedImage[]::new);
}
public void draw(Graphics g) {
- g.drawImage(innerBox, xPos, yPos, PACMAN_SIZE, PACMAN_SIZE, null);
+ g.drawImage(innerBox, position.x, position.y, PACMAN_SIZE, PACMAN_SIZE, null);
//g.setColor(Color.RED);
//g.drawLine(xPos, yPos, xPos, yPos);
//g.drawImage(movmentImages[direction][aniIndex], xPos, yPos, PACMAN_SIZE, PACMAN_SIZE, null);
}
- public void setLeft() {
- direction = LEFT;
- }
-
- public void setRight() {
- direction = RIGHT;
- }
-
- public void setUp() {
- direction = UP;
- }
-
- public void setDown() {
- direction = DOWN;
- }
-
public void update() {
- //System.out.println("Pacman current pos: " + xPos + ", " + yPos);
updateAnimationTick();
- int nextTileX = xPos;
- int nextTileY = yPos;
+ if(direction == Direction.NONE) return;
- switch (direction) {
- case RIGHT: {
- nextTileX++;
- break;
- }
- case LEFT:
- nextTileX--;
- break;
- case UP:
- nextTileY--;
- break;
- case DOWN:
- nextTileY++;
- break;
- }
+ Point newPosition = switch (direction){
+ case RIGHT -> new Point(position.x + speed, position.y);
+ case LEFT -> new Point(position.x - speed, position.y);
+ case UP -> new Point(position.x , position.y - speed);
+ case DOWN -> new Point(position.x, position.y + speed);
+ default -> throw new IllegalStateException("Unexpected value: " + direction);
+ };
+ Point destination = collisionChecker.getValidDestination(direction, newPosition, PACMAN_SIZE, PACMAN_SIZE);
- if (moving && collisionChecker.canMoveTo(direction, nextTileX, nextTileY, PACMAN_SIZE, PACMAN_SIZE)) {
- //boolean b = collisionChecker.canMoveTo(direction, nextTileX, nextTileY, PACMAN_SIZE, PACMAN_SIZE);
- //System.out.println("Can" + (b ? " " : "not ") + "move to (" +nextTileX+ "," + nextTileY + ")");
- updatePosition();
+ if(destination != null) {
+ position = destination;
+ System.out.println("Position: + " + position);
}
}
- private void updatePosition() {
- if(moving) {
- switch (direction) {
- case RIGHT -> {
- if(xPos + PACMAN_SIZE < GamePanel.SCREEN_WIDTH) xPos += speed;
- }
- case LEFT -> {
- if(xPos > 0) xPos -= speed;
- else xPos = 0;
- }
- case DOWN -> {
- if(yPos + PACMAN_SIZE < GamePanel.SCREEN_HEIGHT) yPos += speed;
- }
- case UP ->{
- if(yPos > 0) yPos -= speed;
- }
- }
- }
- }
private void updateAnimationTick() {
if (moving) {
@@ -145,4 +97,9 @@ public class PacMan {
public void setMoving(boolean moving) {
this.moving = moving;
}
+
+ public void setDirection(Direction direction) {
+ this.moving = true;
+ this.direction = direction;
+ }
}
diff --git a/src/main/java/se/urmo/game/input/KeyHandler.java b/src/main/java/se/urmo/game/input/KeyHandler.java
index b495307..3e7c4b8 100644
--- a/src/main/java/se/urmo/game/input/KeyHandler.java
+++ b/src/main/java/se/urmo/game/input/KeyHandler.java
@@ -1,28 +1,29 @@
package se.urmo.game.input;
-import se.urmo.game.GamePanel;
+import se.urmo.game.state.GameStateManager;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class KeyHandler implements KeyListener {
- private final GamePanel gamePanel;
+ private final GameStateManager gameStateManager;
- public KeyHandler(GamePanel gamePanel) {
- this.gamePanel = gamePanel;
+ public KeyHandler(GameStateManager gameStateManager) {
+ this.gameStateManager = gameStateManager;
}
@Override
public void keyTyped(KeyEvent e) {
+ //not in use
}
@Override
public void keyPressed(KeyEvent e) {
- gamePanel.keyPressed(e);
+ gameStateManager.getCurrentState().keyPressed(e);
}
@Override
public void keyReleased(KeyEvent e) {
- gamePanel.keyReleased(e);
+ gameStateManager.getCurrentState().keyReleased(e);
}
}
diff --git a/src/main/java/se/urmo/game/state/CollisionChecker.java b/src/main/java/se/urmo/game/state/CollisionChecker.java
deleted file mode 100644
index fb93ff4..0000000
--- a/src/main/java/se/urmo/game/state/CollisionChecker.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package se.urmo.game.state;
-
-import se.urmo.game.GameMap;
-
-public class CollisionChecker {
- private GameMap map;
-
- public CollisionChecker(GameMap map) {
- this.map = map;
- }
-
- public boolean canMoveTo(int dir, int objectLeft, int objectTop, int width, int height) {
- int objectRight = objectLeft + width;
- int objectBotton = objectTop + height;
- return switch (dir){
- case 0 -> isPassibleRight(objectRight, objectTop, objectBotton);// right
- case 1 -> isPassibleLeft(objectLeft, objectTop, objectBotton);// right
- case 2 -> isPassibleBottom(objectLeft, objectRight, objectBotton);
- case 3 -> isPassibleTop(objectLeft, objectRight, objectTop);// right
- default -> throw new IllegalArgumentException("Invalid dir: " + dir);
- };
- }
-
- private boolean isPassibleBottom(int objectLeft, int objectRight, int objectBotton) {
- return map.isPassable(objectLeft, objectBotton) && map.isPassable(objectRight, objectBotton);
- }
-
- private boolean isPassibleTop(int objectLeft, int objectRight, int objectTop) {
- return map.isPassable(objectLeft, objectTop) && map.isPassable(objectRight, objectTop);
- }
-
- private boolean isPassibleLeft(int objectLeft, int objectTop, int objectBotton) {
- return map.isPassable(objectLeft, objectTop) && map.isPassable(objectLeft, objectBotton);
- }
-
- private boolean isPassibleRight(int objectRight, int objectTop, int objectBotton) {
- return map.isPassable(objectRight, objectTop) && map.isPassable(objectRight, objectBotton);
- }
-}
diff --git a/src/main/java/se/urmo/game/state/GameState.java b/src/main/java/se/urmo/game/state/GameState.java
new file mode 100644
index 0000000..a2b27e4
--- /dev/null
+++ b/src/main/java/se/urmo/game/state/GameState.java
@@ -0,0 +1,13 @@
+package se.urmo.game.state;
+
+import java.awt.*;
+import java.awt.event.KeyEvent;
+
+public interface GameState {
+ void update();
+ void render(Graphics2D g);
+
+ void keyPressed(KeyEvent e);
+
+ void keyReleased(KeyEvent e);
+}
diff --git a/src/main/java/se/urmo/game/state/GameStateManager.java b/src/main/java/se/urmo/game/state/GameStateManager.java
new file mode 100644
index 0000000..aaef185
--- /dev/null
+++ b/src/main/java/se/urmo/game/state/GameStateManager.java
@@ -0,0 +1,35 @@
+package se.urmo.game.state;
+
+import se.urmo.game.Game;
+
+import java.awt.*;
+import java.util.HashMap;
+import java.util.Map;
+
+public class GameStateManager {
+ private final Game game;
+ private Map states = new HashMap<>();
+ private GameState currentState;
+
+ public GameStateManager(Game game) {
+ this.game = game;
+ states.put(GameStateType.PLAYING, new PlayingState(game, this));
+ setState(GameStateType.PLAYING);
+ }
+
+ private void setState(GameStateType type) {
+ currentState = states.get(type);
+ }
+
+ public void update() {
+ currentState.update();
+ }
+
+ public void render(Graphics2D g) {
+ currentState.render(g);
+ }
+
+ public GameState getCurrentState() {
+ return currentState;
+ }
+}
diff --git a/src/main/java/se/urmo/game/state/GameStateType.java b/src/main/java/se/urmo/game/state/GameStateType.java
new file mode 100644
index 0000000..4dcd68b
--- /dev/null
+++ b/src/main/java/se/urmo/game/state/GameStateType.java
@@ -0,0 +1,5 @@
+package se.urmo.game.state;
+
+public enum GameStateType {
+ PLAYING,
+}
diff --git a/src/main/java/se/urmo/game/state/Playing.java b/src/main/java/se/urmo/game/state/Playing.java
deleted file mode 100644
index 38d55ad..0000000
--- a/src/main/java/se/urmo/game/state/Playing.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package se.urmo.game.state;
-
-import se.urmo.game.Game;
-import se.urmo.game.GameMap;
-import se.urmo.game.PacMan;
-
-import java.awt.*;
-import java.awt.event.KeyEvent;
-
-
-public class Playing {
- private PacMan pacMan;
- private GameMap map = new GameMap();
- private final CollisionChecker collisionChecker = new CollisionChecker(map);
- public Playing(Game game) {
- this.pacMan = new PacMan(game, collisionChecker);
- }
-
- public void update() {
- pacMan.update();
- }
-
- public void keyPressed(KeyEvent e) {
- pacMan.setMoving(true);
- switch (e.getKeyCode()) {
- case KeyEvent.VK_A -> pacMan.setLeft();
- case KeyEvent.VK_D -> pacMan.setRight();
- case KeyEvent.VK_W -> pacMan.setUp();
- case KeyEvent.VK_S -> pacMan.setDown();
- }
- }
-
- public void draw(Graphics g) {
- map.draw(g);
- pacMan.draw(g);
- }
-
- public void keyReleased() {
- pacMan.setMoving(false);
- }
- public GameMap getMap() {
- return map;
- }
-}
diff --git a/src/main/java/se/urmo/game/state/PlayingState.java b/src/main/java/se/urmo/game/state/PlayingState.java
new file mode 100644
index 0000000..3314788
--- /dev/null
+++ b/src/main/java/se/urmo/game/state/PlayingState.java
@@ -0,0 +1,46 @@
+package se.urmo.game.state;
+
+import se.urmo.game.*;
+
+import java.awt.*;
+import java.awt.event.KeyEvent;
+
+public class PlayingState implements GameState {
+ private final Game game;
+ private final GameStateManager gameStateManager;
+ private PacMan pacman;
+ private GameMap map;
+ public PlayingState(Game game, GameStateManager gameStateManager) {
+ this.game = game;
+ this.gameStateManager = gameStateManager;
+ this.map = new GameMap();
+ this.pacman = new PacMan(game, new CollisionChecker(map));
+ }
+
+ @Override
+ public void update() {
+ pacman.update();
+ }
+
+ @Override
+ public void render(Graphics2D g) {
+ map.draw(g);
+ pacman.draw(g);
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ switch (e.getKeyCode()) {
+ case KeyEvent.VK_W -> pacman.setDirection(Direction.UP);
+ case KeyEvent.VK_S -> pacman.setDirection(Direction.DOWN);
+ case KeyEvent.VK_A -> pacman.setDirection(Direction.LEFT);
+ case KeyEvent.VK_D -> pacman.setDirection(Direction.RIGHT);
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ pacman.setMoving(false);
+ pacman.setDirection(Direction.NONE);
+ }
+}