所以我正在制作一个类似于Connect 4的游戏。好吧,真的连接4X4。
但是我现在的问题是,我在 JAVA 中绘制了一个网格,当单击单元格时,它会通过绘制一个片段来响应,但该块绘制在错误的位置。
我将主游戏板设置为位于屏幕右侧,它可以完美地检测点击事件,但是当单击时,它会绘制圆圈,就好像网格从屏幕左上角开始一样。
我将在下面发布 GUI 代码。
我的代码基于此处的示例:http://www3.ntu.edu.sg/home/ehchua/programming/java/JavaGame_TicTacToe.html
我知道我的代码很混乱,我没有发布所有代码,但问题出在代码的图形 2D 部分。我知道这也与那里的 x1 和 y1 坐标有关,但我就是想不通。
以防万一我没有解释自己,这里有一个包含我的代码问题的视频:http://www.youtube.com/watch?v=hjcnVl2mjQs#t=0
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
@SuppressWarnings("serial")
public class Board extends JFrame {
// Named-constants for the game board
public static final int ROWS = 4; // ROWS by COLS cells
public static final int COLS = 4;
// Named-constants of the various dimensions used for graphics drawing
public static final int CELL_SIZE = 100; // cell width and height (square)
public static final int CANVAS_WIDTH = CELL_SIZE * COLS; // the drawing canvas
public static final int CANVAS_HEIGHT = CELL_SIZE * ROWS;
public static final int GRID_WIDTH = 2; // Grid-line's width
public static final int GRID_WIDHT_HALF = GRID_WIDTH / 2; // Grid-line's half-width
// Symbols (cross/nought) are displayed inside a cell, with padding from border
public static final int CELL_PADDING = CELL_SIZE / 8;
public static final int SYMBOL_SIZE = CELL_SIZE - CELL_PADDING * 2; // width/height
public static final int SYMBOL_STROKE_WIDTH = 2; // pen's stroke width
int y;
int x;
public Field[] fieldArray = new Field[16];
Field clickedField;
int i = 0;
int boardScreenWidth = 400;
int boardScreenHeight = 400;
int boardStartX = 370;
int boardStartY = 130;
int mouseXPos;
int mouseYPos;
// Use an enumeration (inner class) to represent the various states of the game
public enum GameState {
PLAYING, DRAW, Demon_Won, Angel_Won
}
private GameState currentState; // the current game state
// Use an enumeration (inner class) to represent the seeds and cell contents
public enum Seed {
EMPTY, Angel, Demon
}
private Seed currentPlayer; // the current player
private Seed[][] board ;
// Game board of ROWS-by-COLS cells
private DrawCanvas canvas; // Drawing canvas (JPanel) for the game board
private JLabel statusBar; // Status Bar
/** Constructor to setup the game and the GUI components */
public Board() {
canvas = new DrawCanvas(); // Construct a drawing canvas (a JPanel)
canvas.setPreferredSize(new Dimension(800, 600));
// The canvas (JPanel) fires a MouseEvent upon mouse-click
canvas.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) { // mouse-clicked handler
int mouseX = e.getX();
int mouseY = e.getY();
if (mouseX < boardStartX || mouseX >= boardStartX + boardScreenWidth)
return;
if (mouseY < boardStartY || mouseY >= boardStartY + boardScreenWidth)
return;
//int mouseX = 320; // user clicked at x=320
mouseX = mouseX - boardStartX; // adjust based on board's position
int boardX = mouseX / CELL_SIZE;
// = 310 / 50
// = 6
mouseY = mouseY - boardStartY; // adjust based on board's position
int boardY = mouseY / CELL_SIZE;
// = 310 / 50
// = 6
// Get the row and column clicked
int rowSelected = mouseY / CELL_SIZE;
int colSelected = mouseX / CELL_SIZE;
//System.out.println(rowSelected);
//System.out.println(colSelected);
if (currentState == GameState.PLAYING) {
if (rowSelected >= 0 && rowSelected < ROWS && colSelected >= 0
&& colSelected < COLS && board[rowSelected][colSelected] == Seed.EMPTY) {
board[rowSelected][colSelected] = currentPlayer; // Make a move
updateGame(currentPlayer, rowSelected, colSelected); // update state
// Switch player
currentPlayer = (currentPlayer == Seed.Demon) ? Seed.Angel : Seed.Demon;
}
} else { // game over
initGame(); // restart the game
}
// Refresh the drawing canvas
repaint(); // Call-back paintComponent().
}
});
// Setup the status bar (JLabel) to display status message
statusBar = new JLabel(" ");
statusBar.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD, 30));
statusBar.setBorder(BorderFactory.createEmptyBorder(2, 5, 4, 5));
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(canvas, BorderLayout.CENTER);
cp.add(statusBar, BorderLayout.PAGE_END); // same as SOUTH
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack(); // pack all the components in this JFrame
setTitle("Angels & Demons");
setVisible(true); // show this JFrame
board = new Seed[ROWS][COLS];// allocate array
initGame(); // initialize the game board contents and game variables
}
/** Initialize the game-board contents and the status */
public void initGame() {
for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
board[row][col] = Seed.EMPTY; // all cells empty
}
}
currentState = GameState.PLAYING; // ready to play
currentPlayer = Seed.Demon; // cross plays first
}
/** Update the currentState after the player with "theSeed" has placed on
(rowSelected, colSelected). */
public void updateGame(Seed theSeed, int rowSelected, int colSelected) {
if (hasWon(theSeed, rowSelected, colSelected)) { // check for win
currentState = (theSeed == Seed.Demon) ? GameState.Demon_Won : GameState.Angel_Won;
} else if (isDraw()) { // check for draw
currentState = GameState.DRAW;
}
// Otherwise, no change to current state (still GameState.PLAYING).
}
/** Return true if it is a draw (i.e., no more empty cell) */
public boolean isDraw() {
for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
if (board[row][col] == Seed.EMPTY) {
return false; // an empty cell found, not draw, exit
}
}
}
return true; // no more empty cell, it's a draw
}
/** Return true if the player with "theSeed" has won after placing at
(rowSelected, colSelected) */
public boolean hasWon(Seed theSeed, int rowSelected, int colSelected) {
return (board[rowSelected][0] == theSeed // 3-in-the-row
&& board[rowSelected][1] == theSeed
&& board[rowSelected][2] == theSeed
&& board[rowSelected][3] == theSeed
|| board[0][colSelected] == theSeed // 3-in-the-column
&& board[1][colSelected] == theSeed
&& board[2][colSelected] == theSeed
&& board[3][colSelected] == theSeed
|| rowSelected == colSelected // 3-in-the-diagonal
&& board[0][0] == theSeed
&& board[1][1] == theSeed
&& board[2][2] == theSeed
&& board[3][3] == theSeed
|| rowSelected + colSelected == 3 // 3-in-the-opposite-diagonal
&& board[0][3] == theSeed
&& board[1][2] == theSeed
&& board[2][1] == theSeed
&& board[3][0] == theSeed);
}
/**
* Inner class DrawCanvas (extends JPanel) used for custom graphics drawing.
*/
class DrawCanvas extends JPanel {
@Override
public void paintComponent(Graphics g) { // invoke via repaint()
i=0;
super.paintComponent(g); // fill background
setBackground(Color.WHITE); // set its background color
g.setColor(Color.blue);
//making the filled borders
g.fillRect(30, 560, 740, 30);
g.fillRect(285, 130, 30, 400);
g.fillRect(30, 10, 740, 90);
g.drawRect(30, 350, 200, 180);
// Draw the grid-lines
for (y=130; y<530;y+=100) {
for(x=370; x< 770; x+=100){
g.drawRect(x, y, 100,100);
}
}
//drawing the piece selector
for (y=130; y<280;y+=50){
for(x=30; x < 180; x+=50){
g.drawRect(x, y, 100, 100);
}
}
//populating the piece selector
for (y=130; y<305;y+=50){
for(x=30; x < 205; x+=50){
g.fillOval(x,y, 50, 50);
}
}
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke(SYMBOL_STROKE_WIDTH, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND)); // Graphics2D only
for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
int x1 = col * (CELL_SIZE + CELL_PADDING);
int y1 = row * (CELL_SIZE + CELL_PADDING);
if (board[row][col] == Seed.Demon) {
g2d.setColor(Color.RED);
g2d.fillOval(x1, y1, SYMBOL_SIZE, SYMBOL_SIZE);
} else if (board[row][col] == Seed.Angel) {
g2d.setColor(Color.GREEN);
g2d.fillOval(x1, y1, SYMBOL_SIZE, SYMBOL_SIZE);
}
}
}
// Print status-bar message
if (currentState == GameState.PLAYING) {
statusBar.setForeground(Color.blue);
if (currentPlayer == Seed.Demon) {
statusBar.setText("Player 1's Turn");
} else {
statusBar.setText("Player 2's Turn");
}
} else if (currentState == GameState.DRAW) {
statusBar.setForeground(Color.RED);
statusBar.setText("It's a Draw! Click to play again.");
} else if (currentState == GameState.Demon_Won) {
statusBar.setForeground(Color.RED);
statusBar.setText("'Player 1' Won! Click to play again.");
} else if (currentState == GameState.Angel_Won) {
statusBar.setForeground(Color.RED);
statusBar.setText("'Player 2' Won! Click to play again.");
}
}
}
/** The entry main() method */
public static void main(String args[]) {
// Run GUI codes in the Event-Dispatching thread for thread safety
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Board(); // Let the constructor do the job
}
});
}
}
不应该这样:
int x1 = col * (CELL_SIZE + boardStartX) + CELL_PADDING;
int y1 = row * (CELL_SIZE+ boardStartY) + CELL_PADDING;
而是:
int x1 = col * (CELL_SIZE + CELL_PADDING) + boardStartX;
int y1 = row * (CELL_SIZE + CELL_PADDING) + boardStartY;
或者也许
int x1 = col * (CELL_SIZE + 2 * CELL_PADDING) + boardStartX;
int y1 = row * (CELL_SIZE + 2 * CELL_PADDING) + boardStartY;
如果每个单元格的两侧都有填充。
但话虽如此,为了便于编码,我会以完全不同的方式构建我的游戏/GUI:
- 创建一个 4 x 4 的 JLabels 网格,由使用 GridLayout 的 JPanel 持有。
- 让每个 JLabel 持有一个适当大小的空白图像图标。
- 为每个 JLabel 提供一个侦听鼠标按下事件的 MouseListener。
- 按下 JLabel 时,如果它持有空白图像图标,请将图标与颜色适当的图像图标交换。
- 要重置网格,请将所有图标更改回空白图标。
- 这里不需要覆盖paintComponent或担心在错误的地方绘制。
- 您的彩色磁盘图标很容易制作:创建一个正确大小的缓冲图像,获取其 Graphics2D 对象,设置其渲染提示以使用抗锯齿平滑,绘制彩色圆圈,处理 Graphics2D 对象,并使用缓冲图像创建图像图标。