From 9cf121699f561a8f08e8b9f8f47e05b987aa08ae Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Tue, 9 Jul 2019 09:20:30 +0200 Subject: [PATCH] Separated board and overlay rendering + BoardPane class for managing rendering layers + OverlayComponent for drawing overlays - Renamed BoardPanel to BoardComponent - Simplified calls in the rendering process --- src/dev/kske/chess/game/Game.java | 19 ++- src/dev/kske/chess/game/NaturalPlayer.java | 30 +++-- src/dev/kske/chess/ui/BoardComponent.java | 62 +++++++++ src/dev/kske/chess/ui/BoardPane.java | 55 ++++++++ src/dev/kske/chess/ui/BoardPanel.java | 131 -------------------- src/dev/kske/chess/ui/GameModeDialog.java | 32 ++--- src/dev/kske/chess/ui/MainWindow.java | 10 +- src/dev/kske/chess/ui/OverlayComponent.java | 62 +++++++++ 8 files changed, 227 insertions(+), 174 deletions(-) create mode 100644 src/dev/kske/chess/ui/BoardComponent.java create mode 100644 src/dev/kske/chess/ui/BoardPane.java delete mode 100644 src/dev/kske/chess/ui/BoardPanel.java create mode 100644 src/dev/kske/chess/ui/OverlayComponent.java diff --git a/src/dev/kske/chess/game/Game.java b/src/dev/kske/chess/game/Game.java index 5c2f5f2..cbe4ba8 100644 --- a/src/dev/kske/chess/game/Game.java +++ b/src/dev/kske/chess/game/Game.java @@ -6,7 +6,7 @@ import dev.kske.chess.board.Board; import dev.kske.chess.board.GameState; import dev.kske.chess.board.Move; import dev.kske.chess.board.Piece.Color; -import dev.kske.chess.ui.BoardPanel; +import dev.kske.chess.ui.BoardComponent; /** * Project: Chess
@@ -18,12 +18,12 @@ public class Game { private Map players; private Board board; - private BoardPanel boardPanel; + private BoardComponent boardComponent; - public Game(Map players, Board board, BoardPanel boardPanel) { - this.players = players; - this.board = board; - this.boardPanel = boardPanel; + public Game(Map players, BoardComponent boardComponent) { + this.players = players; + this.boardComponent = boardComponent; + this.board = boardComponent.getBoard(); // Initialize the game variable in each player players.values().forEach(player -> player.setGame(this)); @@ -45,13 +45,10 @@ public class Game { case CHECK: System.out.printf("%s in check!%n", player.color.opposite()); default: - boardPanel.repaint(); + boardComponent.repaint(); players.get(player.color.opposite()).requestMove(); } - } else { - System.out.printf("%s: Illegal move!%n", player.getColor()); - player.requestMove(); - } + } else player.requestMove(); } } diff --git a/src/dev/kske/chess/game/NaturalPlayer.java b/src/dev/kske/chess/game/NaturalPlayer.java index 9229096..63a6f96 100644 --- a/src/dev/kske/chess/game/NaturalPlayer.java +++ b/src/dev/kske/chess/game/NaturalPlayer.java @@ -2,12 +2,14 @@ package dev.kske.chess.game; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.List; +import java.util.stream.Collectors; import dev.kske.chess.board.Board; import dev.kske.chess.board.Move; import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Position; -import dev.kske.chess.ui.BoardPanel; +import dev.kske.chess.ui.OverlayComponent; /** * Project: Chess
@@ -19,10 +21,10 @@ public class NaturalPlayer extends Player { private boolean moveRequested; - public NaturalPlayer(Board board, Color color, BoardPanel boardPanel) { + public NaturalPlayer(Board board, Color color, OverlayComponent overlayComponent) { super(board, color); moveRequested = false; - boardPanel.addMouseListener(new MouseAdapter() { + overlayComponent.addMouseListener(new MouseAdapter() { private Position pos; @@ -30,21 +32,25 @@ public class NaturalPlayer extends Player { public void mousePressed(MouseEvent evt) { if (!moveRequested) return; if (pos == null) { - pos = new Position(evt.getPoint().x / boardPanel.getTileSize(), - evt.getPoint().y / boardPanel.getTileSize()); + pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(), + evt.getPoint().y / overlayComponent.getTileSize()); Board board = (Board) NaturalPlayer.this.board.clone(); - if (board.get(pos) != null && board.get(pos).getColor() == color) - boardPanel.displayMoves(board.getMoves(pos)); - else pos = null; + if (board.get(pos) != null && board.get(pos).getColor() == color) { + List positions = board.getMoves(pos) + .stream() + .map(move -> move.dest) + .collect(Collectors.toList()); + overlayComponent.displayDots(positions); + } else pos = null; } else { - Position dest = new Position(evt.getPoint().x / boardPanel.getTileSize(), - evt.getPoint().y / boardPanel.getTileSize()); + Position dest = new Position(evt.getPoint().x / overlayComponent.getTileSize(), + evt.getPoint().y / overlayComponent.getTileSize()); - boardPanel.clearDisplayMoves(); + overlayComponent.clearDots(); moveRequested = false; game.onMove(NaturalPlayer.this, new Move(pos, dest)); - pos = null; + pos = null; } } }); diff --git a/src/dev/kske/chess/ui/BoardComponent.java b/src/dev/kske/chess/ui/BoardComponent.java new file mode 100644 index 0000000..09b97d7 --- /dev/null +++ b/src/dev/kske/chess/ui/BoardComponent.java @@ -0,0 +1,62 @@ +package dev.kske.chess.ui; + +import java.awt.Color; +import java.awt.Graphics; + +import javax.swing.JComponent; + +import dev.kske.chess.board.Board; + +/** + * Project: Chess
+ * File: BoardComponent.java
+ * Created: 01.07.2019
+ * Author: Kai S. K. Engelbart
+ *
+ * A square panel for rendering the chess board. To work correctly, + * this must be added to a parent component that allows the child to decide the + * size. + */ +public class BoardComponent extends JComponent { + + private static final long serialVersionUID = 6771148331334310216L; + + private final BoardPane boardPane; + + private Board board; + + public BoardComponent(BoardPane boardPane) { + this.boardPane = boardPane; + setSize(boardPane.getPreferredSize()); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + final int tileSize = getTileSize(); + + // Draw the board + g.setColor(Color.white); + for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) { + if (j > 0) g.setColor(g.getColor().equals(Color.white) ? Color.lightGray : Color.white); + g.fillRect(tileSize * i, tileSize * j, tileSize, tileSize); + } + + // Draw the pieces if a board is present + if (board != null) for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) + if (board.getBoardArr()[i][j] != null) g.drawImage(TextureUtil + .getPieceTexture(board.getBoardArr()[i][j]), i * tileSize, j * tileSize, this); + } + + public int getTileSize() { return boardPane.getTileSize(); } + + public Board getBoard() { return board; } + + public void setBoard(Board board) { + this.board = board; + repaint(); + } +} diff --git a/src/dev/kske/chess/ui/BoardPane.java b/src/dev/kske/chess/ui/BoardPane.java new file mode 100644 index 0000000..a2349db --- /dev/null +++ b/src/dev/kske/chess/ui/BoardPane.java @@ -0,0 +1,55 @@ +package dev.kske.chess.ui; + +import java.awt.Dimension; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +import javax.swing.JLayeredPane; + +/** + * Project: Chess
+ * File: BoardPane.java
+ * Created: 08.07.2019
+ * Author: Kai S. K. Engelbart + */ +public class BoardPane extends JLayeredPane { + + private static final long serialVersionUID = -5415058382478806092L; + + private final BoardComponent boardComponent; + private final OverlayComponent overlayComponent; + + private int tileSize; + + public BoardPane() { + boardComponent = new BoardComponent(this); + overlayComponent = new OverlayComponent(this); + + add(boardComponent, Integer.valueOf(1)); + add(overlayComponent, Integer.valueOf(2)); + + /* + * Add a component listener for adjusting the tile size on resizing. + * The size of the board is assumed to be 8x8, as well as the both the board and + * the tiles being square. + */ + addComponentListener(new ComponentAdapter() { + + @Override + public void componentResized(ComponentEvent e) { + tileSize = getWidth() / 8; + TextureUtil.scalePieceTextures(tileSize); + } + }); + setSize(getPreferredSize()); + } + + @Override + public Dimension getPreferredSize() { return new Dimension(480, 480); } + + public BoardComponent getBoardComponent() { return boardComponent; } + + public OverlayComponent getOverlayComponent() { return overlayComponent; } + + public int getTileSize() { return tileSize; } +} diff --git a/src/dev/kske/chess/ui/BoardPanel.java b/src/dev/kske/chess/ui/BoardPanel.java deleted file mode 100644 index 5cf4b3c..0000000 --- a/src/dev/kske/chess/ui/BoardPanel.java +++ /dev/null @@ -1,131 +0,0 @@ -package dev.kske.chess.ui; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JPanel; - -import dev.kske.chess.board.Board; -import dev.kske.chess.board.Move; - -/** - * Project: Chess
- * File: BoardPanel.java
- * Created: 01.07.2019
- * Author: Kai S. K. Engelbart
- *
- * A square panel for rendering the chess board. To work correctly, - * this must be added to a parent component that allows the child to decide the - * size. - */ -public class BoardPanel extends JPanel { - - private static final long serialVersionUID = 6771148331334310216L; - - private int tileSize; - private Board board; - - private List displayMoves; - - public BoardPanel(Board board) { - this(); - setBoard(board); - } - - public BoardPanel() { - displayMoves = new ArrayList<>(); - - /* - * Add a component listener for adjusting the tile size on resizing. - * The size of the board is assumed to be 8x8, as well as the both the board and - * the tiles being square. - */ - addComponentListener(new ComponentAdapter() { - - @Override - public void componentResized(ComponentEvent e) { - tileSize = getWidth() / 8; - TextureUtil.scalePieceTextures(tileSize); - } - }); - - setSize(getPreferredSize()); - } - - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - - // Draw the board - g.setColor(Color.white); - for (int i = 0; i < 8; i++) - for (int j = 0; j < 8; j++) { - if (j > 0) g.setColor(g.getColor().equals(Color.white) ? Color.lightGray : Color.white); - g.fillRect(tileSize * i, tileSize * j, tileSize, tileSize); - } - - // Draw the pieces if a board is present - if (board != null) for (int i = 0; i < 8; i++) - for (int j = 0; j < 8; j++) - if (board.getBoardArr()[i][j] != null) g.drawImage(TextureUtil - .getPieceTexture(board.getBoardArr()[i][j]), i * tileSize, j * tileSize, this); - - // Draw possible moves if a piece was selected - if (!displayMoves.isEmpty()) { - g.setColor(Color.green); - int radius = tileSize / 4; - for (Move move : displayMoves) - g.fillOval(move.dest.x * tileSize + tileSize / 2 - radius / 2, - move.dest.y * tileSize + tileSize / 2 - radius / 2, - radius, - radius); - } - } - - /** - * Displays move destinations on the board. - * - * @param moves The moves to display - */ - public void displayMoves(List moves) { - displayMoves.clear(); - displayMoves.addAll(moves); - repaint(); - } - - /** - * Clears all display moves. - */ - public void clearDisplayMoves() { - displayMoves.clear(); - repaint(); - } - - /** - * Reverts the board to its initial state. - */ - public void reset() { - board.initializeDefaultPositions(); - repaint(); - } - - @Override - public Dimension getMinimumSize() { return getPreferredSize(); } - - @Override - public Dimension getMaximumSize() { return getPreferredSize(); } - - @Override - public Dimension getPreferredSize() { return new Dimension(480, 480); } - - public int getTileSize() { return tileSize; } - - public Board getBoard() { return board; } - - public void setBoard(Board board) { this.board = board; } -} diff --git a/src/dev/kske/chess/ui/GameModeDialog.java b/src/dev/kske/chess/ui/GameModeDialog.java index aee5471..f9d7dcf 100644 --- a/src/dev/kske/chess/ui/GameModeDialog.java +++ b/src/dev/kske/chess/ui/GameModeDialog.java @@ -7,6 +7,7 @@ import java.util.Map; import javax.swing.JButton; import javax.swing.JDialog; +import dev.kske.chess.board.Board; import dev.kske.chess.board.Piece.Color; import dev.kske.chess.game.Game; import dev.kske.chess.game.NaturalPlayer; @@ -26,7 +27,7 @@ public class GameModeDialog extends JDialog { /** * Create the dialog. */ - public GameModeDialog(BoardPanel boardPanel) { + public GameModeDialog(BoardPane boardPane) { super(); setModal(true); setTitle("Game Mode Selection"); @@ -34,12 +35,16 @@ public class GameModeDialog extends JDialog { setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); + final BoardComponent boardComponent = boardPane.getBoardComponent(); + final OverlayComponent overlayComponent = boardPane.getOverlayComponent(); + final Board board = boardComponent.getBoard(); + JButton btnNatural = new JButton("Game against natural opponent"); btnNatural.addActionListener((evt) -> { Map players = new HashMap<>(); - players.put(Color.WHITE, new NaturalPlayer(boardPanel.getBoard(), Color.WHITE, boardPanel)); - players.put(Color.BLACK, new NaturalPlayer(boardPanel.getBoard(), Color.BLACK, boardPanel)); - new Game(players, boardPanel.getBoard(), boardPanel).start(); + players.put(Color.WHITE, new NaturalPlayer(board, Color.WHITE, overlayComponent)); + players.put(Color.BLACK, new NaturalPlayer(board, Color.BLACK, overlayComponent)); + new Game(players, boardComponent).start(); dispose(); }); getContentPane().add(btnNatural); @@ -47,24 +52,21 @@ public class GameModeDialog extends JDialog { JButton btnAI = new JButton("Game against AI"); btnAI.addActionListener((evt) -> { Map players = new HashMap<>(); - players.put(Color.WHITE, new NaturalPlayer(boardPanel.getBoard(), Color.WHITE, boardPanel)); - players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK, 4)); - startGame(players, boardPanel); + players.put(Color.WHITE, new NaturalPlayer(board, Color.WHITE, overlayComponent)); + players.put(Color.BLACK, new AIPlayer(board, Color.BLACK, 4)); + new Game(players, boardComponent).start(); + dispose(); }); getContentPane().add(btnAI); JButton btnAI2 = new JButton("AI against AI"); btnAI2.addActionListener((evt) -> { Map players = new HashMap<>(); - players.put(Color.WHITE, new AIPlayer(boardPanel.getBoard(), Color.WHITE, 4)); - players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK, 3)); - startGame(players, boardPanel); + players.put(Color.WHITE, new AIPlayer(board, Color.WHITE, 4)); + players.put(Color.BLACK, new AIPlayer(board, Color.BLACK, 3)); + new Game(players, boardComponent).start(); + dispose(); }); getContentPane().add(btnAI2); } - - private void startGame(Map players, BoardPanel boardPanel) { - new Game(players, boardPanel.getBoard(), boardPanel).start(); - dispose(); - } } diff --git a/src/dev/kske/chess/ui/MainWindow.java b/src/dev/kske/chess/ui/MainWindow.java index 8784f42..9948987 100644 --- a/src/dev/kske/chess/ui/MainWindow.java +++ b/src/dev/kske/chess/ui/MainWindow.java @@ -53,19 +53,19 @@ public class MainWindow { mframe.setBounds(100, 100, 494, 565); mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - BoardPanel boardPanel = new BoardPanel(new Board()); - boardPanel.setLayout(null); - mframe.getContentPane().add(boardPanel, BorderLayout.CENTER); + BoardPane boardPane = new BoardPane(); + boardPane.getBoardComponent().setBoard(new Board()); + mframe.getContentPane().add(boardPane, BorderLayout.CENTER); JPanel toolPanel = new JPanel(); mframe.getContentPane().add(toolPanel, BorderLayout.NORTH); JButton btnRestart = new JButton("Restart"); - btnRestart.addActionListener((evt) -> boardPanel.reset()); + btnRestart.addActionListener((evt) -> System.err.println("Resetting not implemented!")); toolPanel.add(btnRestart); mframe.pack(); // Display dialog for game mode selection - new GameModeDialog(boardPanel).setVisible(true); + new GameModeDialog(boardPane).setVisible(true); } } diff --git a/src/dev/kske/chess/ui/OverlayComponent.java b/src/dev/kske/chess/ui/OverlayComponent.java new file mode 100644 index 0000000..86557ad --- /dev/null +++ b/src/dev/kske/chess/ui/OverlayComponent.java @@ -0,0 +1,62 @@ +package dev.kske.chess.ui; + +import java.awt.Color; +import java.awt.Graphics; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JComponent; + +import dev.kske.chess.board.Position; + +/** + * Project: Chess
+ * File: OverlayComponent.java
+ * Created: 08.07.2019
+ * Author: Kai S. K. Engelbart + */ +public class OverlayComponent extends JComponent { + + private static final long serialVersionUID = -7326936060890082183L; + + private final BoardPane boardPane; + + private List dots; + + public OverlayComponent(BoardPane boardPane) { + this.boardPane = boardPane; + setSize(boardPane.getPreferredSize()); + dots = new ArrayList<>(); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + final int tileSize = getTileSize(); + + // Draw possible moves if a piece was selected + if (!dots.isEmpty()) { + g.setColor(Color.green); + int radius = tileSize / 4; + for (Position dot : dots) + g.fillOval(dot.x * tileSize + tileSize / 2 - radius / 2, + dot.y * tileSize + tileSize / 2 - radius / 2, + radius, + radius); + } + } + + public void displayDots(List dots) { + this.dots.clear(); + this.dots.addAll(dots); + repaint(); + } + + public void clearDots() { + dots.clear(); + repaint(); + } + + public int getTileSize() { return boardPane.getTileSize(); } +}