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
This commit is contained in:
Kai S. K. Engelbart 2019-07-09 09:20:30 +02:00
parent 3dfc16943f
commit 9cf121699f
8 changed files with 227 additions and 174 deletions

View File

@ -6,7 +6,7 @@ import dev.kske.chess.board.Board;
import dev.kske.chess.board.GameState; import dev.kske.chess.board.GameState;
import dev.kske.chess.board.Move; import dev.kske.chess.board.Move;
import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.ui.BoardPanel; import dev.kske.chess.ui.BoardComponent;
/** /**
* Project: <strong>Chess</strong><br> * Project: <strong>Chess</strong><br>
@ -18,12 +18,12 @@ public class Game {
private Map<Color, Player> players; private Map<Color, Player> players;
private Board board; private Board board;
private BoardPanel boardPanel; private BoardComponent boardComponent;
public Game(Map<Color, Player> players, Board board, BoardPanel boardPanel) { public Game(Map<Color, Player> players, BoardComponent boardComponent) {
this.players = players; this.players = players;
this.board = board; this.boardComponent = boardComponent;
this.boardPanel = boardPanel; this.board = boardComponent.getBoard();
// Initialize the game variable in each player // Initialize the game variable in each player
players.values().forEach(player -> player.setGame(this)); players.values().forEach(player -> player.setGame(this));
@ -45,13 +45,10 @@ public class Game {
case CHECK: case CHECK:
System.out.printf("%s in check!%n", player.color.opposite()); System.out.printf("%s in check!%n", player.color.opposite());
default: default:
boardPanel.repaint(); boardComponent.repaint();
players.get(player.color.opposite()).requestMove(); players.get(player.color.opposite()).requestMove();
} }
} else { } else player.requestMove();
System.out.printf("%s: Illegal move!%n", player.getColor());
player.requestMove();
}
} }
} }

View File

@ -2,12 +2,14 @@ package dev.kske.chess.game;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; 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.Board;
import dev.kske.chess.board.Move; import dev.kske.chess.board.Move;
import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.board.Position; import dev.kske.chess.board.Position;
import dev.kske.chess.ui.BoardPanel; import dev.kske.chess.ui.OverlayComponent;
/** /**
* Project: <strong>Chess</strong><br> * Project: <strong>Chess</strong><br>
@ -19,10 +21,10 @@ public class NaturalPlayer extends Player {
private boolean moveRequested; private boolean moveRequested;
public NaturalPlayer(Board board, Color color, BoardPanel boardPanel) { public NaturalPlayer(Board board, Color color, OverlayComponent overlayComponent) {
super(board, color); super(board, color);
moveRequested = false; moveRequested = false;
boardPanel.addMouseListener(new MouseAdapter() { overlayComponent.addMouseListener(new MouseAdapter() {
private Position pos; private Position pos;
@ -30,21 +32,25 @@ public class NaturalPlayer extends Player {
public void mousePressed(MouseEvent evt) { public void mousePressed(MouseEvent evt) {
if (!moveRequested) return; if (!moveRequested) return;
if (pos == null) { if (pos == null) {
pos = new Position(evt.getPoint().x / boardPanel.getTileSize(), pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(),
evt.getPoint().y / boardPanel.getTileSize()); evt.getPoint().y / overlayComponent.getTileSize());
Board board = (Board) NaturalPlayer.this.board.clone(); Board board = (Board) NaturalPlayer.this.board.clone();
if (board.get(pos) != null && board.get(pos).getColor() == color) if (board.get(pos) != null && board.get(pos).getColor() == color) {
boardPanel.displayMoves(board.getMoves(pos)); List<Position> positions = board.getMoves(pos)
else pos = null; .stream()
.map(move -> move.dest)
.collect(Collectors.toList());
overlayComponent.displayDots(positions);
} else pos = null;
} else { } else {
Position dest = new Position(evt.getPoint().x / boardPanel.getTileSize(), Position dest = new Position(evt.getPoint().x / overlayComponent.getTileSize(),
evt.getPoint().y / boardPanel.getTileSize()); evt.getPoint().y / overlayComponent.getTileSize());
boardPanel.clearDisplayMoves(); overlayComponent.clearDots();
moveRequested = false; moveRequested = false;
game.onMove(NaturalPlayer.this, new Move(pos, dest)); game.onMove(NaturalPlayer.this, new Move(pos, dest));
pos = null; pos = null;
} }
} }
}); });

View File

@ -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: <strong>Chess</strong><br>
* File: <strong>BoardComponent.java</strong><br>
* Created: <strong>01.07.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong><br>
* <br>
* 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();
}
}

View File

@ -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: <strong>Chess</strong><br>
* File: <strong>BoardPane.java</strong><br>
* Created: <strong>08.07.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong>
*/
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; }
}

View File

@ -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: <strong>Chess</strong><br>
* File: <strong>BoardPanel.java</strong><br>
* Created: <strong>01.07.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong><br>
* <br>
* 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<Move> 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<Move> 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; }
}

View File

@ -7,6 +7,7 @@ import java.util.Map;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JDialog; import javax.swing.JDialog;
import dev.kske.chess.board.Board;
import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.game.Game; import dev.kske.chess.game.Game;
import dev.kske.chess.game.NaturalPlayer; import dev.kske.chess.game.NaturalPlayer;
@ -26,7 +27,7 @@ public class GameModeDialog extends JDialog {
/** /**
* Create the dialog. * Create the dialog.
*/ */
public GameModeDialog(BoardPanel boardPanel) { public GameModeDialog(BoardPane boardPane) {
super(); super();
setModal(true); setModal(true);
setTitle("Game Mode Selection"); setTitle("Game Mode Selection");
@ -34,12 +35,16 @@ public class GameModeDialog extends JDialog {
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); 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"); JButton btnNatural = new JButton("Game against natural opponent");
btnNatural.addActionListener((evt) -> { btnNatural.addActionListener((evt) -> {
Map<Color, Player> players = new HashMap<>(); Map<Color, Player> players = new HashMap<>();
players.put(Color.WHITE, new NaturalPlayer(boardPanel.getBoard(), Color.WHITE, boardPanel)); players.put(Color.WHITE, new NaturalPlayer(board, Color.WHITE, overlayComponent));
players.put(Color.BLACK, new NaturalPlayer(boardPanel.getBoard(), Color.BLACK, boardPanel)); players.put(Color.BLACK, new NaturalPlayer(board, Color.BLACK, overlayComponent));
new Game(players, boardPanel.getBoard(), boardPanel).start(); new Game(players, boardComponent).start();
dispose(); dispose();
}); });
getContentPane().add(btnNatural); getContentPane().add(btnNatural);
@ -47,24 +52,21 @@ public class GameModeDialog extends JDialog {
JButton btnAI = new JButton("Game against AI"); JButton btnAI = new JButton("Game against AI");
btnAI.addActionListener((evt) -> { btnAI.addActionListener((evt) -> {
Map<Color, Player> players = new HashMap<>(); Map<Color, Player> players = new HashMap<>();
players.put(Color.WHITE, new NaturalPlayer(boardPanel.getBoard(), Color.WHITE, boardPanel)); players.put(Color.WHITE, new NaturalPlayer(board, Color.WHITE, overlayComponent));
players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK, 4)); players.put(Color.BLACK, new AIPlayer(board, Color.BLACK, 4));
startGame(players, boardPanel); new Game(players, boardComponent).start();
dispose();
}); });
getContentPane().add(btnAI); getContentPane().add(btnAI);
JButton btnAI2 = new JButton("AI against AI"); JButton btnAI2 = new JButton("AI against AI");
btnAI2.addActionListener((evt) -> { btnAI2.addActionListener((evt) -> {
Map<Color, Player> players = new HashMap<>(); Map<Color, Player> players = new HashMap<>();
players.put(Color.WHITE, new AIPlayer(boardPanel.getBoard(), Color.WHITE, 4)); players.put(Color.WHITE, new AIPlayer(board, Color.WHITE, 4));
players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK, 3)); players.put(Color.BLACK, new AIPlayer(board, Color.BLACK, 3));
startGame(players, boardPanel); new Game(players, boardComponent).start();
dispose();
}); });
getContentPane().add(btnAI2); getContentPane().add(btnAI2);
} }
private void startGame(Map<Color, Player> players, BoardPanel boardPanel) {
new Game(players, boardPanel.getBoard(), boardPanel).start();
dispose();
}
} }

View File

@ -53,19 +53,19 @@ public class MainWindow {
mframe.setBounds(100, 100, 494, 565); mframe.setBounds(100, 100, 494, 565);
mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BoardPanel boardPanel = new BoardPanel(new Board()); BoardPane boardPane = new BoardPane();
boardPanel.setLayout(null); boardPane.getBoardComponent().setBoard(new Board());
mframe.getContentPane().add(boardPanel, BorderLayout.CENTER); mframe.getContentPane().add(boardPane, BorderLayout.CENTER);
JPanel toolPanel = new JPanel(); JPanel toolPanel = new JPanel();
mframe.getContentPane().add(toolPanel, BorderLayout.NORTH); mframe.getContentPane().add(toolPanel, BorderLayout.NORTH);
JButton btnRestart = new JButton("Restart"); JButton btnRestart = new JButton("Restart");
btnRestart.addActionListener((evt) -> boardPanel.reset()); btnRestart.addActionListener((evt) -> System.err.println("Resetting not implemented!"));
toolPanel.add(btnRestart); toolPanel.add(btnRestart);
mframe.pack(); mframe.pack();
// Display dialog for game mode selection // Display dialog for game mode selection
new GameModeDialog(boardPanel).setVisible(true); new GameModeDialog(boardPane).setVisible(true);
} }
} }

View File

@ -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: <strong>Chess</strong><br>
* File: <strong>OverlayComponent.java</strong><br>
* Created: <strong>08.07.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong>
*/
public class OverlayComponent extends JComponent {
private static final long serialVersionUID = -7326936060890082183L;
private final BoardPane boardPane;
private List<Position> 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<Position> dots) {
this.dots.clear();
this.dots.addAll(dots);
repaint();
}
public void clearDots() {
dots.clear();
repaint();
}
public int getTileSize() { return boardPane.getTileSize(); }
}