Reputation: 93
So I am making a game which is like Connect 4. Well Connect 4X4 really.
But my problem now is that I have a grid drawn in JAVA that responds by drawing a piece when clicked on a cell but the piece is drawn in the wrong place.
I have the main game board set up to be located towards the right hand side of the screen, and it detects click events perfectly, but when clicked it draws the circles as if the grid starts at the top left of the screen.
I'll post the GUI code below.
My code is based off of sample found here: http://www3.ntu.edu.sg/home/ehchua/programming/java/JavaGame_TicTacToe.html
I know my code is quite messy and I didn't post all of it but the problem lies within the Graphics 2D part of the code. And I know its also to do with the x1 and y1 coordinates down there but I just can't figure it out.
Just in case I haven't explained myself here's a video with my code problem: 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
}
});
}
}
Upvotes: 1
Views: 3450
Reputation: 285403
Shouldn't this:
int x1 = col * (CELL_SIZE + boardStartX) + CELL_PADDING;
int y1 = row * (CELL_SIZE+ boardStartY) + CELL_PADDING;
Instead be:
int x1 = col * (CELL_SIZE + CELL_PADDING) + boardStartX;
int y1 = row * (CELL_SIZE + CELL_PADDING) + boardStartY;
or maybe
int x1 = col * (CELL_SIZE + 2 * CELL_PADDING) + boardStartX;
int y1 = row * (CELL_SIZE + 2 * CELL_PADDING) + boardStartY;
If you have padding on both sides of each cell.
But having said this, I would structure my game/GUI completely differently for ease of coding:
Upvotes: 4