Roreo
Roreo

Reputation: 93

Drawing circles on a 2D array grid (JAVA)

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

Answers (1)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

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:

  • Create a 4 x 4 grid of JLabels held by a JPanel that uses GridLayout.
  • Have each JLabel hold a blank ImageIcon of the appropriate size.
  • Give each JLabel a MouseListener that listens for mousePressed events.
  • When a JLabel is pressed, if it holds a blank ImageIcon, swap Icons with an appropriately colored ImageIcon.
  • To reset the grid, change all Icons back to the blank one.
  • No need here to override paintComponent or to worry about drawing in the wrong place.
  • Your colored disk icons would be easy to make: create a BufferedImage of the correct size, get its Graphics2D object, set its rendering hints to smooth out with anti-aliasing, draw a colored circle, dispose of the Graphics2D object, and create an ImageIcon with the BufferedImage.

Upvotes: 4

Related Questions