为什么当我运行代码时面板会打开,但它永远不会到达actionPerformed函数

问题描述 投票:0回答:1

panel view of my 2048 game我正在尝试制作2048游戏。我制作了终端版本来检查我的游戏逻辑,一切似乎都正常。现在我添加了 GameFrame 和 GamePanel 类。我的目标是能够单击箭头键将图块移动到所需的方向。我当前的问题是,运行我的代码时它永远不会到达我的 actionPerformed 函数。我正在使用阻塞队列来等待用户决定下一步行动。运行我的代码时,面板会显示两个初始图块(应该如此),但是当我单击任何箭头时,没有任何变化。通过 print 语句,我可以看到它没有到达 actionPerformed 函数,但我不明白为什么。根据我的理解,这个函数应该每 75 毫秒运行一次(这是我选择的延迟),并且我设置了 setFocusable(true),所以我认为它应该监听键盘事件。在另一篇文章中,我读到了一个类似的问题,它说要使用 addActionListener,但我不确定如果我缺少什么,我应该在哪里添加它。任何帮助表示赞赏!我将添加所有 GamePanel、GameFrame 类和 main:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;


public class GamePanel extends JPanel implements ActionListener {


    static final int SCREEN_WIDTH = 600;
    static final int SCREEN_HEIGHT = 600;
    static final int DELAY = 75;
    BlockingQueue<Boolean> queue;
    Timer timer;
    boolean running = false;
    Random random;
    Board grid;



    GamePanel(){

        random = new Random();
        this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
        this.setBackground(Color.white);
        this.setFocusable(true);
        this.queue = new LinkedBlockingQueue<>();
        this.addKeyListener(new MyKeyAdapter());
        startGame();

    }


    public void startGame(){
        grid = new Board();
        grid.newTile();
        grid.newTile();
        running = true;
        timer = new Timer(DELAY, this);
        timer.start();
        System.out.println("game started");
    }


    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        int tileSize = SCREEN_WIDTH / this.grid.SIZE; // Calculate tile size based on panel size and board size

        for (int row = 0; row < this.grid.SIZE; row++) {
            for (int col = 0; col < this.grid.SIZE; col++) {
                int x = col * tileSize;
                int y = row * tileSize;
                g2d.drawRect(x, y, tileSize, tileSize);
            }
}

        for (int row = 0; row < this.grid.SIZE; row++) {
            for (int col = 0; col < this.grid.SIZE; col++) {
                int x = col * tileSize;
                int y = row * tileSize;
                Tile tile = grid.board[row][col];
                if (tile != null) {
                    g2d.setColor(tile.getColor());
                    g2d.fillRect(x, y, tileSize, tileSize);
                    g2d.setColor(Color.BLACK);
                    g2d.drawString(String.valueOf(tile.getValue()), x + tileSize / 2, y + tileSize / 2);
            }
        }
    }
}

     @Override
    public void actionPerformed(ActionEvent e) {
        try{
            boolean running = queue.take();
            System.out.println("I am in the try");
        if(running){
            System.out.println("running is true");
            grid.move();
            grid.newTile();
            if (!grid.gridHasSpace()) {
                //TODO Handle game over logic here 
                queue.put(false);
                }
        } 
        repaint();
    } catch (InterruptedException e1){
        //TODO
    }
    }



    public class MyKeyAdapter extends KeyAdapter{

        @Override
        public void keyPressed(KeyEvent e){
            switch (e.getKeyCode()) {
                case KeyEvent.VK_LEFT:
                    grid.setDirection('L');
                    System.out.println("Left key pressed");
                    break;
                case KeyEvent.VK_RIGHT:
                    grid.setDirection('R');
                    break;
                case KeyEvent.VK_UP:
                    grid.setDirection('U');
                    break;
                case KeyEvent.VK_DOWN:
                    grid.setDirection('D');
                    break;
            }
            try {
                queue.put(true);
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
    
}
}
import javax.swing.JFrame;

public class GameFrame extends JFrame {
    
    GameFrame(){
        this.add(new GamePanel());
        this.setTitle("Game2048");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setResizable(false);
        this.pack();
        this.setVisible(true);
        this.setLocationRelativeTo(null);
        
    }
}
import javax.swing.SwingUtilities;

public class Game2048 {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {GameFrame gameFrame = new GameFrame();});
    }
java oop jpanel event-listener 2048
1个回答
0
投票

来自 JavaDocs 的

BlockQueue

E采取() 抛出 InterruptedException

检索并删除此队列的头部,必要时等待,直到元素变得可用

(重点是我添加的)

在您的代码上下文中,

take
将阻塞事件调度线程,防止发生任何进一步的事件处理(即按键或绘制事件)。

在现有的背景下,这并没有什么意义。如果您要做的只是等待状态更改,为什么要使用

Timer
?为什么不简单地在触发时使用按键处理程序触发方法呢?

我也不鼓励使用

KeyListener
,如果没有其他原因,那么它创建的所有与焦点相关的问题都已由 Keybindings API 解决了。

这还允许您将操作与源/触发器解耦,并允许您重新使用逻辑。这可能意味着您还可以将按钮控件添加到 UI 作为用户输入的另一种方式,并为按钮和键绑定重新使用

Action

作为一个概念示例...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new GamePanel());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GamePanel extends JPanel { //implements ActionListener {
        enum Direction {
            LEFT, RIGHT, UP, DOWN;
        }

        static final int SCREEN_WIDTH = 600;
        static final int SCREEN_HEIGHT = 600;
        private boolean running = false;
        private Random random;

        GamePanel() {
            random = new Random();
            this.setBackground(Color.white);
            //this.queue = new LinkedBlockingQueue<>();

            InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap actionMap = getActionMap();

            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), Direction.LEFT);
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), Direction.RIGHT);
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), Direction.UP);
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), Direction.DOWN);

            actionMap.put(Direction.LEFT, new GridAction('L'));
            actionMap.put(Direction.RIGHT, new GridAction('R'));
            actionMap.put(Direction.UP, new GridAction('U'));
            actionMap.put(Direction.UP, new GridAction('D'));

            startGame();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT);
        }

        public void startGame() {
            running = true;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //int tileSize = SCREEN_WIDTH / this.grid.SIZE; // Calculate tile size based on panel size and board size
            //
            //for (int row = 0; row < this.grid.SIZE; row++) {
            //    for (int col = 0; col < this.grid.SIZE; col++) {
            //        int x = col * tileSize;
            //        int y = row * tileSize;
            //        g2d.drawRect(x, y, tileSize, tileSize);
            //    }
            //}
            //
            //for (int row = 0; row < this.grid.SIZE; row++) {
            //    for (int col = 0; col < this.grid.SIZE; col++) {
            //        int x = col * tileSize;
            //        int y = row * tileSize;
            //        Tile tile = grid.board[row][col];
            //        if (tile != null) {
            //            g2d.setColor(tile.getColor());
            //            g2d.fillRect(x, y, tileSize, tileSize);
            //            g2d.setColor(Color.BLACK);
            //            g2d.drawString(String.valueOf(tile.getValue()), x + tileSize / 2, y + tileSize / 2);
            //        }
            //    }
            //}
        }

        protected void directionDidChange(char direction) {
            if (!running) {
                return;
            }
            //grid.setDirection(direction);
            // Do what ever other processing your need to do
            repaint();
        }

        protected class GridAction extends AbstractAction {
            // I'd use a enum for this purpose, but that's me
            private char direction;

            public GridAction(char action) {
                this.direction = action;
            }

            public char getDirection() {
                return direction;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                directionDidChange(getDirection());
            }
        }
    }
}

如果您需要

Timer
根据当前状态执行不同的操作,那么我会利用
Set
来跟踪当前按下了哪些键。这将要求您跟踪新闻/发布状态,因此它确实增加了一些复杂性。 例如

© www.soinside.com 2019 - 2024. All rights reserved.