MouseListener для экземпляра игровой панели - PullRequest
0 голосов
/ 27 апреля 2020

Я создаю игру Ti c Ta c Toe, в которой я использую MouseListener для добавления функциональности на свою игровую панель. Когда пользователь щелкает одну из ячеек, предполагается, что он генерирует граф X или O c в зависимости от того, чья это очередь. Я пытался добавить MouseListener на панель, но когда я запускаю его, ничего не происходит, когда я нажимаю. Любые идеи о том, как я могу это исправить?

Вот моя игровая панель:

public GameMain() {
        Handler handler = new Handler();
        this.addMouseListener(handler);

        // setup JLabel
        label = new JLabel("         ");
        label.setBorder(BorderFactory.createEmptyBorder(2, 5, 4, 5));
        label.setOpaque(true);
        label.setBackground(Color.LIGHT_GRAY);

        setLayout(new BorderLayout());
        add(label, BorderLayout.SOUTH);
        setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT + 30));
        board = new Board();
        initGame();

    }   
}

Вот мой класс Handler с методом mouseClick (), который предполагается запустить:

public class Handler extends MouseAdapter {
        public void mouseClick(MouseEvent e) {
            int mouseX = e.getX();
            int mouseY = e.getY();

            // Get the row and column clicked
            int rowSelected = mouseY / CELL_SIZE;
            int colSelected = mouseX / CELL_SIZE;

            if (currentState == GameState.PLAYING) {
                if (rowSelected >= 0 && rowSelected < board.cells.length && colSelected >= 0 && colSelected < board.cells.length &&  
                    board.cells[rowSelected][colSelected].content == Seed.EMPTY) {
                    board.cells[rowSelected][colSelected].content = currentPlayer; // move
                    updateGame(currentPlayer, rowSelected, colSelected); // update currentState
                    currentPlayer = (currentPlayer == Seed.X) ? Seed.O : Seed.X;
                }
            } else {
                initGame();
            }

            repaint();
        }

        public void handleButtonPress(Object o) {
            if (o == singlePlayer) {
                singlePlayerGame();
            }
            if (o == multiPlayer) {
                multiPlayerGame();
            }
        }
    }

1 Ответ

1 голос
/ 28 апреля 2020

Ваш вопрос вдохновил меня на то, чтобы взять на себя задачу написания ti c -ta c -toe игры, так как у меня нет большого опыта работы с пользовательской живописью. Приведенный ниже код обильно прокомментирован, поэтому я надеюсь, что эти комментарии послужат хорошим объяснением кода.

Я предполагаю, что люди с большим опытом, чем я, найдут fl aws в приведенном ниже коде , чего и следовало ожидать, так как, как я уже упоминал, у меня нет большого опыта в этом виде программирования. Тем не менее, я надеюсь, что это достаточно хорошо, чтобы помочь вам.

Обратите внимание, что в приведенном ниже коде используется новая функция java, которая была представлена ​​в JDK 14, а именно Records . Следовательно, он также может быть полезен людям как простой пример того, как интегрировать записи java в их код. Если вы не используете JDK 14 или не используете функции предварительного просмотра , вы можете просто заменить определение записи простым классом. Просто замените слово record на class и добавьте в код конструктор и методы "getter". По умолчанию имя метода «getter» в record - это просто имя поля, например, для элемента minX в записи Square (в коде ниже), метод «getter» - minX() .

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

/**
 * A simple tic-tac-toe (a.k.a. noughts and crosses) game. User clicks mouse on an empty square of
 * the game board and either a nought or a cross is drawn in that square, depending on whose turn
 * it is. Each square has an index as shown below.
 * 
 * 0    1   2
 * 
 * 3    4   5
 * 
 * 6    7   8
 * 
* См. Ti c -ta c -ee на Wikipedia . * / publi c класс GameMain расширяет JPanel реализует ActionListener, MouseListener, Runnable {/ ** Для сериализации экземпляров этого класса. * / личное состояние c окончательный длинный serialVersionUID = 7014608855599083001L; / ** Крест. * / private stati c final char CROSS = 'X'; / ** Ничего. * / private stati c final char NAUGHT = 'O'; / ** Размер каждого квадрата в доске c -ta c. * / private stati c final int SQUARE_DIMENSION = 80; / ** Количество последовательных квадратов, необходимых для победы. * / private stati c final int REQUIRED_SQUARES = 3; / ** Общее количество квадратов в доске c -ta c. * / private stati c final int TOTAL_SQUARES = 9; / ** Каждый из 9 квадратов на доске c -ta c. * / частная конечная площадь [] SQUARES = новая площадь [] {новая площадь (1, 1, 99, 99), новая площадь (101, 1, 199, 99), новая площадь (201, 1, 299, 99), Новая площадь (1, 101, 99, 199), Новая площадь (101, 101, 199, 199), Новая площадь (201, 101, 299, 199), Новая площадь (1, 201, 99, 299), Новая площадь (101, 201, 199, 299), новая площадь (201, 201, 299, 299)}; / ** Текст для {@link #turnLabel} в начале новой игры ti c -ta c. * / private stati c final String FIRST_MOVE = "X идет первым"; / ** текст для новая игра . кнопка. * / private stati c final String NEW_GAME = "Новая игра"; / ** Указывает на начало новой игры. * / private boolean newGame; / ** true означает поворот O, а false означает поворот X * / приватный логический oTurn; / ** Записывает занятые квадраты, либо 'O', либо 'X' * / private char [] busy = new char [TOTAL_SQUARES]; / ** Количество незанятых квадратов на доске c -ta c. * / private int freeCount; / ** Отображает чей ход в данный момент. * / частный JLabel turnLabel; / ** Местоположение последнего щелчка мыши. * / private Point clickPoint; / ** * Создает и возвращает экземпляр этого класса. * / publi c GameMain () {setPreferredSize (новое измерение (300, 300)); addMouseListener (это); freeCount = TOTAL_SQUARES; } private stati c boolean isValidRequirement (int index) { возвращаемый индекс> = 0 && index квадрат действительным индексом квадрата в доске ti c -ta c. * * @param square - будет подтвержден. * * @return true , если квадрат является действительным. * / private stati c логическое значение isValidSquare (int square) {return square> = 0 && square {Текст строки; if (isWinner ()) {text = oTurn? "O выиграл!": "X выиграл!"; RemoveMouseListener (this); JOptionPane.showMessageDialog (this, text, "Winner", JOptionPane.PLAIN_MESSAGE);} иначе if (freeCount <= 0) {text = "Drawn game."; RemoveMouseListener (this); JOptionPane.showMessageDialog (this, text, "Draw", JOptionPane.PLAIN_MESSAGE);} else {oTurn =! oTurn; text = oTurn? "Поворот O": "Поворот X";} turnLabel.setText (text);}); } @Override // java .lang.Runnable publi c void run () {createAndShow Gui (); } @Override // javax.swing.JComponent protected void paintComponent (Graphics g) {super.paintComponent (g); Graphics2D g2d = (Graphics2D) g; g2d.drawLine (100, 0, 100, 300); g2d.drawLine (200, 0, 200, 300); g2d.drawLine (0, 100, 300, 100); g2d.drawLine (0, 200, 300, 200); if (! newGame) {g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setStroke (new BasicStroke (3)); for (int i = 0; i <TOTAL_SQUARES; i ++) {if (оккупированный [i] == NAUGHT) {drawNought (i, g2d); } else if (busy [i] == CROSS) {drawCross (i, g2d); }} int square = getSquare (clickPoint); if (isFreeSquare (square)) {if (oTurn) {drawNought (square, g2d); занято [квадрат] = NAUGHT; } else {drawCross (square, g2d); занято [квадрат] = крест; } freeCount--; }} else {newGame = false; }} private void createAndShow Gui () {JFrame frame = new JFrame ("O & X"); frame.setDefaultCloseOperation (WindowConstants.EXIT_ON_CLOSE); frame.add (createTurnPanel (), BorderLayout.PAGE_START); frame.add (this, BorderLayout.CENTER); frame.add (createButtonsPanel (), BorderLayout.PAGE_END); frame.pack (); frame.setLocationByPlatform (истина); frame.setVisible (истина); } private JButton createButton (String text, int mnemoni c, String tooltip) {JButton button = new JButton (text); if (mnemoni c> 0) {button.setMnemoni c (mnemoni c); } if (tooltip! = null &&! tooltip.isBlank ()) {button.setToolTipText (tooltip); } button.addActionListener (this); кнопка возврата; } private JPanel createButtonsPanel () {JPanel buttonsPanel = new JPanel (); buttonsPanel.setBorder (BorderFactory.createMatteBorder (2, 0, 0, 0, Color.GRAY)); buttonsPanel.add (createButton (NEW_GAME, KeyEvent.VK_N, «Начать новую игру.»)); кнопка возврата панели; } приватный JPanel createTurnPanel () {JPanel turnPanel = new JPanel (); turnPanel.setBorder (BorderFactory.createMatteBorder (0, 0, 2, 0, Color.GRAY)); turnLabel = новый JLabel (FIRST_MOVE); turnPanel.add (turnLabel); возврат TurnPanel; } / ** * Dr aws a {@link #CROSS} в квадрате доски c -ta c. * * @param square - индекс квадрата в доске c -ta c. * @param g2d - облегчает рисование. * / private void drawCross (int square, Graphics2D g2d) {if (isValidSquare (square) && g2d! = null) {g2d.setColor (Color.BLUE); g2d.drawLine (КВАДРАТЫ [квадрат] .minX () + 10, КВАДРАТЫ [квадрат] .minY () + 10, КВАДРАТЫ [квадрат] .maxX () - 10, КВАДРАТЫ [квадрат] .maxY () - 10); g2d.drawLine (КВАДРАТЫ [квадрат] .maxX () - 10, КВАДРАТЫ [квадрат] .minY () + 10, КВАДРАТЫ [квадрат] .minX () + 10, КВАДРАТЫ [квадрат] .maxY () - 10); }} / ** * Dr aws a {@link #NOUGHT} в квадрате доски c -ta c. * * @param square - индекс квадрата в доске c -ta c. * @param g2d - облегчает рисование. * / private void drawNought (int square, Graphics2D g2d) {if (isValidSquare (square) && g2d! = null) {g2d.setColor (Color.RED); g2d.drawOval (SQUARES [квадрат] .minX () + 10, SQUARES [квадрат] .minY () + 10, SQUARE_DIMENSION, SQUARE_DIMENSION); }} / ** * Возвращает квадрат платы ti c -ta c, которая содержит pt . * * @param pt - точка на доске c -ta c. * * @ возвращаем индекс квадрата в ti c -ta c - доска, содержащая pt или -1 (отрицательный) * если pt не в t * c -та c -тое доска. * * @see Square # содержит (int, int) * / private int getSquare (Point pt) {int ndx = -1; if (pt! = null) {for (int i = 0; i <9; i ++) {if (SQUARES [i] .contains (pt.x, pt.y)) {ndx = i; перемена; }}} return ndx; } / ** * Определяет, содержит ли <var>столбец таблицы ti c -ta c все элементы {@link #CROSS} или все * {@link #NOUGHT}. * * @param column - индекс столбца на доске c -ta c. * * @return true , если столбец содержит все {@link #CROSS} или все {@link #NOUGHT}. * * @see #isValidRequirement (int) * / private boolean isColumnWin (int column) {boolean isWin = false; if (isValidRequirement (column)) {isWin = isOccupied (column) && оккупированный [column] == занятый [column + REQUIRED_SQUARES] && оккупированный [column] == занятый [column + (REQUIRED_SQUARES * 2)]; } return isWin; } / ** * Определяет, содержит ли диагональ платы ti c -ta c все элементы {@link #CROSS} или все * {@link #NOUGHT}. Доска содержит ровно две диагонали, каждая из которых включает центральный квадрат * доски, то есть квадрат с индексом 4. Остальные квадраты, составляющие * диагонали, являются угловыми квадратами, включая индексы 0 (ноль), 2, 6 и 8. * * @return true , если одна из диагоналей доски ti c -ta c содержит все {@link #CROSS} * или все {@link #NOUGHT}. * * @see #isValidRequirement (int) * / private boolean isDiagonalWin () {boolean isWin = false; isWin = (isOccupied (0) && оккупировано [0] == занято [4] && занято [0] == занято [8]) || (isOccupied (2) && занимает [2] == занято [4] && занято [2] == занято [6]); возврат isWin; } / ** * Определяет, не содержит ли квадрат на плате ti c -ta c -etoe {@link #CROSS} * или {@link #NOUGHT}. * * @param square - индекс квадрата в доске c -ta c. * * @return true , если квадрат не содержит ни {@link #CROSS}, ни {@link * #NOUGHT}. * / private логическое isFreeSquare (int square) {логическое freeSquare = false; if (isValidSquare (квадрат)) {freeSquare = занято [квадрат]! = КРОСС && занято [квадрат]! = NAUGHT; } вернуть freeSquare; } / ** * Определяет, содержит ли строка доски ti c -ta c все элементы {@link #CROSS} или все * {@link #NOUGHT}. * * @param row - индекс строки на доске c -ta c. * * @return true , если строка содержит все {@link #CROSS} или все {@link #NOUGHT}. * * @see #isValidRequirement (int) * / private boolean isLineWin (int row) {boolean isWin = false; if (isValidRequirement (row)) {int index = row * REQUIRED_SQUARES; isWin = isOccupied (index) && занимаемые [index] == занятые [index + 1] && занимаемые [index] == занимаемые [index + 2]; } return isWin; } / ** * Определяет, содержит ли квадрат в index на доске ti c -ta c - или {@link * #CROSS}, или {@link #NOUGHT}. * * @param index - индекс квадрата в доске c -ta c. * * @return true если квадрат в index в ti c -ta c -панель содержит либо * {@link #CROSS}, либо {@link # НИЧЕГО}. * * @see #isValidSquare (int) * / private boolean isOccupied (int index) {boolean busy = false; if (isValidSquare (index)) {busy = this.occupied [index] == CROSS || this.occupied [index] == NAUGHT; } возвращение занято; } / ** * Определяет, есть ли победитель. * * @return true , если кто-то выиграл игру. * * @see #isColumnWin (int) * @see #isDiagonalWin () * @see #isLineWin (int) * / private boolean isWinner () {return isLineWin (0) || isLineWin (1) || isLineWin (2) || isColumnWin (0) || isColumnWin (1) || isColumnWin (2) || isDiagonalWin (); } / ** * Инициализирует GUI, чтобы начать новую игру. * / private void startNewGame () {freeCount = TOTAL_SQUARES; newGame = true; oTurn = false; оккупированный = новый символ [TOTAL_SQUARES]; перекрасить (); EventQueue.invokeLater (() -> {removeMouseListener (this); addMouseListener (this); turnLabel.setText (FIRST_MOVE);}); } / ** * Этот метод вызывается первым, когда этот класс запускается с помощью команды java *. Он игнорирует параметр метода args . * * @param args - java аргументы команды. * / publi c stati c void main (String [] args) {EventQueue.invokeLater (new GameMain ()); }} / ** * Представляет геометрическую форму, известную как квадрат. Для целей этой {@code record} квадрат * определяется двумя точками, которые указывают его верхний левый угол и нижний правый угол. * / квадрат записи (int minX, int minY, int maxX, int maxY) {/ ** * Определяет, находится ли поставленная точка в этом квадрате. * * @param x - x координата точки. * @param y - y координата той же точки (как x). * * @return true , если поставленная точка находится внутри этого квадрата. * / publi c логическое значение содержит (int x, int y) {return minX <= x && x <= maxX && minY <= y && y <= maxY; }} </code>
...