java - 使用图形和 Java OOP 的类似于 Tic Tac Toe 的游戏的问题

标签 java swing oop graphics

我正在开发我 friend 发明的一款游戏 - 井字游戏的一个小变体:使用 4x4 棋盘,一个玩家 (x) 需要以特定方式获得 3 个 x,而另一个玩家可以放置一个“z”每回合一个“O”,需要填满整个棋盘。我的问题不在于规则和算法,而在于图形:我在图形方面没有很多经验,并且无法让我的电路板工作(即使没有任何规则 - 只需根据需要显示即可)。

我有一个代表董事会的 Board 类。一 block 板有一个二维的单元阵列。每个单元格(单元格=我的另一个类)也是一个 JButton,我希望每次单击按钮时他的图像都会改变 - 所以我决定使用 ImageIcon。我还有一个 GameMain 类来控制游戏,还有一个 Tools 类来添加两个按钮 -“退出”和“重置”。

如果您能帮助我建议如何让我的主板正确加载,我将不胜感激。目前,该板根本不显示,如果我稍微调整一下代码,它会显示,但按钮根本不会显示。

这是代码:GameMain.java:

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class GameMain extends JPanel {
    private Turn _turn;
    Board _board;
    private Tools _buttons;
    private boolean isOver = false;

    public enum GameState {PLAYING, xWON, oWON};

    private GameState _currentState;

    // Name-constants for 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 * 4;  // the drawing canvas
    public static final int CANVAS_HEIGHT = CELL_SIZE * 4;
    public static final int GRID_WIDTH = 8;  // Grid-line's width
    public static final int GRID_WIDTH_HALF = GRID_WIDTH / 2; // Grid-line's half-width

    public GameMain() {
        this.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (_currentState == GameState.PLAYING) {
                    updateGame();
                } else {
                    initGame(); //game over, restart
                }
                repaint();
            }
        });

        setLayout(new BorderLayout());
        setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT + 30));
        _board = new Board();
        _buttons = new Tools();
        initGame();
        _buttons.SetObject(_board);

        add(_board, BorderLayout.CENTER);
        add(_buttons, BorderLayout.SOUTH);
    }

    public void initGame() {
        _turn = Turn.X;
        _board.init();
        _currentState = GameState.PLAYING;
    }

    public void updateGame() {
        if (_board.hasWonX()) {
            _currentState = GameState.xWON;
        } else if (_board.hasWonO()) {
            _currentState = GameState.oWON;
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        setBackground(Color.WHITE);

        _board.paint(g);
    }


    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("xBlock");
                frame.setSize(500, 500);
                // Set the content-pane of the JFrame to an instance of main JPanel
                frame.setContentPane(new GameMain());
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null); // center the application window
                frame.setVisible(true);            // show it
            }
        });
    }
}

董事会:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class Board extends JPanel implements ActionListener {
    private Cell[][] _cells;
    private Turn _turn;

    public Board() {
        setLayout(new GridLayout(4, 4));

        _cells = new Cell[4][4];
        _turn = Turn.X;

        for (int i = 0; i < _cells.length; i++) {
            for (int j = 0; j < _cells[0].length; j++) {
                _cells[i][j] = new Cell(i, j);
                _cells[i][j].addActionListener(this);
                add(_cells[i][j]);
            }
        }
    }

    //initiate board
    public void init() {
        _turn = Turn.X;
        for (int i = 0; i < _cells.length; i++) {
            for (int j = 0; j < _cells[0].length; j++) {
                _cells[i][j].setState(State.EMPTY);
            }
        }

    }

    public void fillCell(Cell c) {
        if (c.getState() == State.EMPTY) {
            c.setState(_turn.ordinal());
            c.setEnabled(false);
            c.draw();
            _turn = _turn.getNext();
        }
    }

    public void checkCellsAround(Cell c) {
        State state = c.getState();
        State right, left, up, down;

        if (c.getJ() < 3 && c.getJ() > 0) {
            right = _cells[c.getI()][c.getJ() + 1].getState();
            left = _cells[c.getI()][c.getJ() - 1].getState();
        } else if (c.getJ() == 0) {
            right = _cells[c.getI()][c.getJ() + 1].getState();
            left = State.EMPTY;
        } else {
            right = State.EMPTY;
            left = _cells[c.getI()][c.getJ() - 1].getState();
        }

        if (c.getI() < 3 && c.getI() > 0) {
            up = _cells[c.getI() - 1][c.getJ()].getState();
            down = _cells[c.getI() + 1][c.getJ()].getState();
        } else if (c.getI() == 0) {
            up = State.EMPTY;
            down = _cells[c.getI() + 1][c.getJ()].getState();
        } else {
            up = _cells[c.getI() - 1][c.getJ()].getState();
            down = State.EMPTY;
        }

        switch (state) {
            case EMPTY:
                break;
            case X:
                if ((left == State.O && right == State.O) || (up == State.O && down == State.O) || (left == State.Z && right == State.Z) || (up == State.Z && down == State.Z)) {
                    c.setState(State.HOURGLASS);
                }
                break;
            case O:
                if ((left == State.X && right == State.X) || (up == State.X && down == State.X)) {
                    c.setState(State.EMPTY);
                }
                break;
            case Z:
                if ((left == State.X && right == State.X) || (up == State.X && down == State.X)) {
                    c.setState(State.HOURGLASS);
                }
                break;
            case HOURGLASS:
                break;
            case SCRIBBLE:
                break;
        }

    }

    public void actionPerformed(ActionEvent E) {
        Cell c = (Cell) E.getSource();
        fillCell(_cells[c.getI()][c.getJ()]);
    }

    public boolean hasWonO() {
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if (_cells[i][j].getState() == State.EMPTY) {
                    return false;
                }
            }
        }
        return true;
    }

    public boolean hasWonX() {
        return false;
    }


    public void paint(Graphics g) {
        g.setColor(Color.GRAY);
        for (int i = 1; i < 4; i++) {
            g.fillRoundRect(0, GameMain.CELL_SIZE * i - GameMain.GRID_WIDTH_HALF,
                    GameMain.CANVAS_WIDTH - 1, GameMain.GRID_WIDTH,
                    GameMain.GRID_WIDTH, GameMain.GRID_WIDTH);
        }
        for (int j = 1; j < 4; j++) {
            g.fillRoundRect(GameMain.CELL_SIZE * j - GameMain.GRID_WIDTH_HALF, 0,
                    GameMain.GRID_WIDTH, GameMain.CANVAS_HEIGHT - 1,
                    GameMain.GRID_WIDTH, GameMain.GRID_WIDTH);
        }
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                _cells[i][j].draw();
            }
        }
    }
}

Cell.java:

    import javax.swing.ImageIcon;
import javax.swing.JButton;

public class Cell extends JButton {
    private int _i, _j;
    private State _state;

    ImageIcon X = new ImageIcon(this.getClass().getResource("x-icon.png"));
    ImageIcon O = new ImageIcon(this.getClass().getResource("o-icon.png"));
    ImageIcon Z = new ImageIcon(this.getClass().getResource("z-icon.png"));
    ImageIcon Hourglass = new ImageIcon(this.getClass().getResource("hourglass-icon.png"));
    ImageIcon Scribble = new ImageIcon(this.getClass().getResource("scribble-icon.png"));

    public Cell() {
        this.setEnabled(true);
        _i = 0;
        _j = 0;
        _state = State.EMPTY;

    }

    public Cell(int i, int j) {
        this.setEnabled(true);
        _i = i;
        _j = j;
        _state = State.EMPTY;
    }

    public int getI() {
        return _i;
    }

    public int getJ() {
        return _j;
    }

    public void setState(State state) {
        _state = state;
        if (state == State.EMPTY) {
            this.setEnabled(true);
        }
    }

    public void setState(int index) {
        _state = State.values()[index];
    }

    public State getState() {
        return _state;
    }

    public void draw() {
        switch (_state) {
            case EMPTY:
                this.setIcon(null);
                break;

            case X:
                this.setIcon(X);
                break;

            case O:
                this.setIcon(X);
                break;

            case Z:
                this.setIcon(X);
                break;

            case HOURGLASS:
                this.setIcon(X);
                break;

            case SCRIBBLE:
                this.setIcon(X);
                break;
        }
    }

    public void highlight() {
    }
}

工具.java:

 import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class Tools extends JPanel {

    private JButton _exit, _reset;
    private Board _board;

    Tools() {
        setLayout(new FlowLayout());

        _exit = new JButton("Exit");
        _reset = new JButton("Reset");

        _exit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                System.exit(0);
            }
        });

        _reset.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                _board.init();
            }
        });
        add(_exit);
        add(_reset);
    }

    public void SetObject(Board b) {
        _board = b;
    }

}

状态.java:

public enum State {
    EMPTY, X, O, Z, HOURGLASS, SCRIBBLE;


    public State getNext() {
        return State.values()[(this.ordinal() + 1) % State.values().length];
    }
}

Turn.java:

public enum Turn {
    X, O, Z;

    public Turn getNext() {
        return Turn.values()[(this.ordinal() + 1) % Turn.values().length];
    }
}

提前致谢!

最佳答案

运行后,您将得到 ArrayIndexOutOfBoundsExceptionBoard的paint方法中的这一行类:

    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            _cells[i][j].draw();       <==========
        }
    }

不确定您的游戏是如何工作的,但通过查看此循环之前的循环,您最多只能访问索引 3 ( for (int j = 1; j < 4; j++) { )。因此,如果将最大循环次数更改为 4,游戏就会启动并运行。

    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            _cells[i][j].draw();
        }
    }

学习读取异常和堆栈跟踪非常重要。这将为您省去很多 future 的麻烦。花一些时间阅读What is a stack trace, and how can I use it to debug my application errors?


正如我在评论中所说,

It doesn't look like you're doing anything with the paintComponent in the GameMain. You should just get rid of the paintComponent method altogether in that class. Instead of trying to call board.paint(g);, try to just call board.repaint() in the mouse listener, instead of trying to repaint the main game panel. And just set the background in the constructor of the GameMain instead of in the paintComponent method.

Also in the Board class use paintComponent rather than paint. and don't forget to call super.paintComponent in the paintComponent method

解决上述所有问题,让它发挥作用(我猜)。


更新

正如 MadMan 在下面的评论中指出的那样,最好使用 _cells.length以避免依赖魔数(Magic Number)。这样您就可以确保不会访问不存在的索引

for (int i = 0; i < cells.length; i++) {
    for (int j = 0; j < cells[i].length; j++) {
        _cells[i][j].draw();
    }
}

关于java - 使用图形和 Java OOP 的类似于 Tic Tac Toe 的游戏的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26323586/

相关文章:

java - 通过 JMX 调用操作会出现 NoAccessRuntimeException

java - 调整 JFrame 大小以适合 JPanel

java - 使用 StyledDocument.insertString 将富文本附加到 JTextPane 不会保留字体

php - PHP 高级 OOP 特性的真实示例

java - 如何检查数组是否已满,如果未满则添加到它?

java - 更改字段后从 Set 中删除对象

java - 数组(java.lang.ArrayIndexOutOfBoundsException :-1)

C++ 类设计为每个不同的行为提供多个接口(interface)

java - 如何通过 eclipse RCP 中的不同执行来更新和维护查看器状态?

java - JTable 中的复选框选择