Draw things after board is drawn with paint (JPanel) (java)

1.3k views Asked by At

I am trying to make a very simple Tic Tac Toe game in Java. However, I cannot figure out how to draw something after I've drawn the board. Here's my structure of my game.

public class ThreeInARowMain extends JPanel {

    public static final int WIDTH = 600, HEIGHT = 640;

    public void paint(Graphics g) {
        g.setColor(Color.white);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.setColor(Color.black);
        g.drawString("1.", 0,20);
        g.drawString("2.", 210,20);
        g.drawString("3.", 410,20);
        g.drawString("4.", 0,220);
        g.drawString("5.", 210,220);
        g.drawString("6.", 410,220);
        g.drawString("7.", 0,420);
        g.drawString("8.", 210,420);
        g.drawString("9.", 410,420);
        //Horizontal Lines
        g.drawLine(0, 200, WIDTH, 200);
        g.drawLine(0, 400, WIDTH, 400);
        //Vertical Lines
        g.drawLine(200, 0, 200, HEIGHT);
        g.drawLine(400, 0, 400, HEIGHT);

    }

    public static void main (String [] args) {
    boolean firstorsecond = true;
        JFrame frame = new JFrame("Tic Tac Toe");
        frame.setSize(WIDTH, HEIGHT);
        frame.getContentPane().add(new ThreeInARowMain());
        frame.setLocationRelativeTo(null);
        frame.setBackground(Color.black);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        boolean someonewins = false;
        int firstplayerpos, secondplayerpos;
        int [] [] board = new int [3] [3];
        Scanner in = new Scanner(System.in);

        while (!someonewins){
        System.out.println("P1, enter the number of the square you'd like to mark.");
        firstplayerpos = in.nextInt();
        DrawXorO(firstplayerpos,firstorsecond);


        System.out.println("P2, enter the number of the square you'd like to mark.");
        secondplayerpos = in.nextInt();
        DrawXorO(secondplayerpos,!firstorsecond);

        }
    }

    public static void DrawXorO (int position, boolean firstorsecond) {

    }

}

I know this is a badly designed game at the moment; I will correct that later. However, what I want with DrawXorO is to draw an X or and O in the position that the player has typed in. How do I add graphics after using the method paint? Thanks!

3

There are 3 answers

4
Gilbert Le Blanc On

Do you mean something like this?

Tic Tac Toe GUI

Here's one way to draw the tic tac toe diagram on the left of the GUI. I have a model class that keeps the board position in a 2 dimensional int array, and a mouse controller class that let's me know where the person clicked on the tic tac toe diagram.

package com.ggl.tictactoe.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import javax.swing.JPanel;

import com.ggl.tictactoe.controller.MouseMoveController;
import com.ggl.tictactoe.model.GameStatus;
import com.ggl.tictactoe.model.TicTacToeModel;

public class TicTacToePanel extends JPanel {

    private static final long serialVersionUID = -9150412950439480699L;

    private MouseMoveController controller;

    private TicTacToeModel model;

    public TicTacToePanel(TicTacToeFrame frame, TicTacToeModel model) {
        this.model = model;
        this.controller = new MouseMoveController(frame, model);
        this.addMouseListener(controller);
        this.setPreferredSize(new Dimension(360, 360));
    }

    public void setComputerMove() {
        if (!model.isPlayerGoesFirst()) {
            controller.setComputerMove();
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, getWidth(), getHeight());

        int[][] board = model.getBoard();
        int width = getWidth() / board.length;
        int height = getHeight() / board[0].length;

        int x = 0;
        int y = 0;

        Stroke stroke = new BasicStroke(7F);
        g2d.setStroke(stroke);
        g2d.setColor(Color.BLACK);

        x += width;
        for (int i = 0; i < (board.length - 1); i++) {
            g2d.drawLine(x, y, x, getHeight());
            x += width;
        }

        x = 0;
        y += width;
        for (int i = 0; i < (board.length - 1); i++) {
            g2d.drawLine(x, y, getWidth(), y);
            y += width;
        }

        float fontSize = (float) height * 72F / 96F;
        Font largeFont = getFont().deriveFont(fontSize);

        for (int i = 0; i < board.length; i++) {
            x = width * i;
            for (int j = 0; j < board[i].length; j++) {
                y = height * j;
                Rectangle r = new Rectangle(x, y, width, height);
                if (board[i][j] == 1) {
                    g2d.setColor(Color.BLUE);
                    centerString(g2d, r, "X", largeFont);
                } else if (board[i][j] == -1) {
                    g2d.setColor(Color.CYAN);
                    centerString(g2d, r, "O", largeFont);
                }
            }
        }

        if (model.getGameStatus() == GameStatus.TIE_GAME) {
            BufferedImage image = GameOverImage.createImage(getWidth(),
                    getHeight(), "Tie Game");
            g2d.drawImage(image, 0, 0, this);
        } else if (model.getGameStatus() == GameStatus.COMPUTER_WINS) {
            BufferedImage image = GameOverImage.createImage(getWidth(),
                    getHeight(), "Computer Wins");
            g2d.drawImage(image, 0, 0, this);
        } else if (model.getGameStatus() == GameStatus.PLAYER_WINS) {
            BufferedImage image = GameOverImage.createImage(getWidth(),
                    getHeight(), "Player Wins");
            g2d.drawImage(image, 0, 0, this);
        }
    }

    /**
     * This method centers a <code>String</code> in a bounding
     * <code>Rectangle</code>.
     * 
     * @param g2d
     *            - The <code>Graphics2D</code> instance.
     * @param r
     *            - The bounding <code>Rectangle</code>.
     * @param s
     *            - The <code>String</code> to center in the bounding rectangle.
     * @param font
     *            - The display font of the <code>String</code>
     * 
     * @see java.awt.Graphics
     * @see java.awt.Rectangle
     * @see java.lang.String
     */
    private void centerString(Graphics2D g2d, Rectangle r, String s, Font font) {
        FontRenderContext frc = new FontRenderContext(null, true, true);

        Rectangle2D r2D = font.getStringBounds(s, frc);
        int rWidth = (int) Math.round(r2D.getWidth());
        int rHeight = (int) Math.round(r2D.getHeight());
        int rX = (int) Math.round(r2D.getX());
        int rY = (int) Math.round(r2D.getY());

        int a = (r.width / 2) - (rWidth / 2) - rX;
        int b = (r.height / 2) - (rHeight / 2) - rY;

        g2d.setFont(font);
        g2d.drawString(s, r.x + a, r.y + b);
    }

}

And here's the mouse controller code.

package com.ggl.tictactoe.controller;

import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import com.ggl.tictactoe.model.GameStatus;
import com.ggl.tictactoe.model.TicTacToeModel;
import com.ggl.tictactoe.view.TicTacToeFrame;

public class MouseMoveController extends MouseAdapter {

    private ComputerMoveRunnable controller;

    private TicTacToeFrame frame;

    private TicTacToeModel model;

    public MouseMoveController(TicTacToeFrame frame, TicTacToeModel model) {
        this.frame = frame;
        this.model = model;
        this.controller = new ComputerMoveRunnable(frame, model);
    }

    @Override
    public void mousePressed(MouseEvent event) {
        if ((model.getGameStatus() == GameStatus.ACTIVE_GAME)
                && (event.getButton() == MouseEvent.BUTTON1)) {
            int width = frame.getTicTacToePanel().getWidth();
            int height = frame.getTicTacToePanel().getHeight();
            int positionWidth = TicTacToeModel.getPositionWidth();

            Point p = event.getPoint();

            int x = getPosition(p.x, width, positionWidth);
            int y = getPosition(p.y, height, positionWidth);

            int[][] board = model.getBoard();
            if (board[x][y] == 0) {
                model.setPlayerMove(x, y);
                setComputerMove();
            }
        }
    }

    public void setComputerMove() {
        controller.run();
        frame.repaintTicTacToePanel();
    }

    private int getPosition(int location, int dimension, int squareWidth) {
        for (int i = 0; i < squareWidth; i++) {
            if (location < (dimension * (i + 1) / squareWidth)) {
                return i;
            }
        }

        return squareWidth - 1;
    }

}
2
MadProgrammer On
2
Lightsout On

I would redesign your GUI to utilize 9 JButtons, 1 for each square. You can then call JButton methods e.g. JButton.setText("X") to make a square display X which would be much simpler. If you really want to override paintComponent, you can create variables to store what each square is currently displaying, update those variables and call repaint() when you need an update.