键绑定动作在开始时是 运行 但不是从实际按键开始

Keybind action being run at start but not from actual key press

我正在为 Java 中的期末项目编写游戏代码,我们的老师为我们提供了一个棋盘 class,它是一个允许我们在虚拟游戏中放置和移除钉子的组件板,而不必自己编写代码。我正在尝试将键绑定添加到 Board 组件,但是当我 运行 程序时,我想要在按键上执行的操作正在发生,但当我键入键时它不会 运行。

开发板 class 已经有一种方法可以让点击组件的位置,我认为这可能会干扰我的代码,但我不确定。

这是我的游戏class,我尝试在其中添加键绑定

package rpgGame;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.KeyStroke;

public class RPGGame
{
    public static final GameWorld WORLD_MAP = new GameWorld();
    public static Board LOCAL_MAP = new Board(20,50);

    public static List<Mobile> allMobs = new ArrayList<Mobile>();
    public static final Player PLAYER = new Player();

    public static int xIndex = ((GameWorld.WORLD_SIZE-1)/2) - (50/2);
    public static int yIndex = ((GameWorld.WORLD_SIZE-1)/2) - (20/2);

    public static boolean boardUpdate = true;

    public enum Direction {RIGHT,LEFT,UP,DOWN}

    private static final String MOVE_PLAYER_UP = "move up";
    private static final String MOVE_PLAYER_LEFT = "move left";
    private static final String MOVE_PLAYER_RIGHT = "move right";
    private static final String MOVE_PLAYER_DOWN = "move down";

    public static final Thread SYNC_BOARD = new Thread()
    {
        public synchronized void run()
        {
            while (boardUpdate)
            {
                for (int i = 0; i < 50; i++)
                {
                    for (int j = 0; j < 20; j++)
                    {
                        if (WORLD_MAP.isOccupied(i+xIndex, j+yIndex)) 
                        {
                            LOCAL_MAP.putPeg(Color.RED, j, i);
                            System.out.println("Successfully Updated");
                        }
                        else
                        {
                            LOCAL_MAP.putPeg(Color.GRAY, j,i);

                        }
                    }
                }
                boardUpdate = false;
            }
        }
    };

    public RPGGame()
    {
        generateMobs(200);
        placeMobs();
        placePlayer();
        SYNC_BOARD.run();

        LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), MOVE_PLAYER_UP);
        LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), MOVE_PLAYER_UP);
        LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), MOVE_PLAYER_LEFT);
        LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), MOVE_PLAYER_LEFT);
        LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), MOVE_PLAYER_RIGHT);
        LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), MOVE_PLAYER_RIGHT);
        LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), MOVE_PLAYER_DOWN);
        LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), MOVE_PLAYER_DOWN);

        LOCAL_MAP.getActionMap().put(MOVE_PLAYER_UP, new MoveAction(Direction.UP));
        LOCAL_MAP.getActionMap().put(MOVE_PLAYER_LEFT, new MoveAction(Direction.LEFT));
        LOCAL_MAP.getActionMap().put(MOVE_PLAYER_RIGHT, new MoveAction(Direction.RIGHT));
        LOCAL_MAP.getActionMap().put(MOVE_PLAYER_DOWN, new MoveAction(Direction.DOWN));
    }

    public static void main(String[] args) 
    {
        new RPGGame();
    }

    public static void generateMobs(int numOfMobs)
    {
        for (int i=0; i<numOfMobs; i++)
        {
            allMobs.add(new Mobile());
        }
    }

    public static void generateMobs()
    {
        int numOfMobs  = (int)(Math.random()*500);
        for (int i=0;i<numOfMobs; i++)
        {
            allMobs.add(new Mobile());
        }
    }

    public static void placeMobs()
    {
        for (int i=0; i<allMobs.size(); i++)
        {
            //i is used as a placeholder value for points until I create a random number generator.
            WORLD_MAP.placeCharacter(i, i,allMobs.get(i));
            allMobs.get(i).setLocation(i, i);
        }
    }

    public static void placePlayer()
    {
        WORLD_MAP.placeCharacter(249, 249, PLAYER);
        PLAYER.setLocation(249, 249);
    }

    @SuppressWarnings("serial")
    public class MoveAction extends AbstractAction
    {
        Direction direction;

        public MoveAction(Direction direction)
        {
            if (direction.equals(Direction.RIGHT))
            {
                int x = PLAYER.getX();
                int y = PLAYER.getY();
                WORLD_MAP.moveCharacter(x+1, y, x, y);
                PLAYER.move(1, 0);
                boardUpdate = true;
                System.out.println("MOVE RIGHT");
            }
            if (direction.equals(Direction.LEFT))
            {
                int x = PLAYER.getX();
                int y = PLAYER.getY();
                WORLD_MAP.moveCharacter(x, y, x-1, y);
                PLAYER.move(-1, 0);
                boardUpdate = true;
                System.out.println("MOVE LEFT");
            }
            if (direction.equals(Direction.UP))
            {
                int x = PLAYER.getX();
                int y = PLAYER.getY();
                WORLD_MAP.moveCharacter(x, y, x, y+1);
                PLAYER.move(0, 1);
                boardUpdate = true;
                System.out.println("MOVE UP");
            }
            if (direction.equals(Direction.DOWN))
            {
                int x = PLAYER.getX();
                int y = PLAYER.getY();
                WORLD_MAP.moveCharacter(x, y, x, y-1);
                PLAYER.move(0, -1);
                boardUpdate = true;
                System.out.println("MOVE DOWN");
            }
        }

        @Override
        public void actionPerformed(ActionEvent e)
        {

        }

    }
}

这是董事会class

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

/**  Board GUI for implementation with various games
 *   Author: Kirill Levin, Troy Vasiga, Chris Ingram
 */

@SuppressWarnings("serial")
public class Board extends JPanel
{
    private static final int X_DIM = 60;
    private static final int Y_DIM = 60;
    private static final int X_OFFSET = 30;
    private static final int Y_OFFSET = 30;
    private static final double MIN_SCALE = 0.25;
    private static final int GAP = 10;
    private static final int FONT_SIZE = 16;

    // Grid colours
    private static final Color GRID_COLOR_A = new Color(153,255,102);
    private static final Color GRID_COLOR_B = new Color(136,255,77);

    private Color[][] grid;
    private Point lastClick;  // How the mouse handling thread communicates 
    // to the board where the last click occurred
    private String message = "";
    private int numLines = 0;
    private double[][] line = new double[4][100];  // maximum number of lines is 100
    private int columns, rows;

    private int originalWidth;
    private int originalHeight;
    private double scale;

    /** A constructor to build a 2D board.
     */
    public Board (int rows, int columns)
    {
        super( true );
        JFrame boardFrame = new JFrame( "Board game" );

        this.columns = columns;
        this.rows = rows;
        originalWidth = 2*X_OFFSET+X_DIM*columns;
        originalHeight = 2*Y_OFFSET+Y_DIM*rows+GAP+FONT_SIZE;

        this.setPreferredSize( new Dimension( originalWidth, originalHeight ) );

        boardFrame.setResizable(true);

        this.grid = new Color[columns][rows];

        this.addMouseListener(
                new MouseInputAdapter() 
                {
                    /** A method that is called when the mouse is clicked
                     */
                    public void mouseClicked(MouseEvent e) 
                    { 
                        int x = (int)e.getPoint().getX();
                        int y = (int)e.getPoint().getY();

                        // We need to by synchronized to the parent class so we can wake
                        // up any threads that might be waiting for us
                        synchronized(Board.this) 
                        {
                            int curX = (int)Math.round(X_OFFSET*scale);
                            int curY = (int)Math.round(Y_OFFSET*scale);
                            int nextX = (int)Math.round((X_OFFSET+X_DIM*grid.length)*scale);
                            int nextY = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale);

                            // Subtract one from high end so clicks on the black edge
                            // don't yield a row or column outside of board because of
                            // the way the coordinate is calculated.
                            if (x >= curX && y >= curY && x < nextX && y < nextY)
                            {
                                lastClick = new Point(y,x);
                                // Notify any threads that would be waiting for a mouse click
                                Board.this.notifyAll() ;
                            } /* if */
                        } /* synchronized */
                    } /* mouseClicked */
                } /* anonymous MouseInputAdapater */
                );

        boardFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        boardFrame.setContentPane( this );
        boardFrame.pack();
        boardFrame.setVisible(true);
    }

    /** A constructor to build a 1D board.
     */
    public Board (int cols)
    {
        this(1, cols);
    }

    private void paintText(Graphics g)
    {
        g.setColor( this.getBackground() );
        g.setFont(new Font(g.getFont().getFontName(), Font.ITALIC+Font.BOLD, (int)(Math.round(FONT_SIZE*scale))));

        int x = (int)Math.round(X_OFFSET*scale);
        int y = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale + GAP  ) ;

        g.fillRect(x,y, this.getSize().width, (int)Math.round(GAP+FONT_SIZE*scale) );
        g.setColor( Color.black );
        g.drawString(message, x, y + (int)Math.round(FONT_SIZE*scale));
    }

    private void paintGrid(Graphics g)
    {
        for (int i = 0; i < this.grid.length; i++)
        {
            for (int j = 0; j < this.grid[i].length; j++)
            {    
                if ((i%2 == 0 && j%2 != 0) || (i%2 != 0 && j%2 == 0))
                    g.setColor(GRID_COLOR_A);
                else
                    g.setColor(GRID_COLOR_B);
                int curX = (int)Math.round((X_OFFSET+X_DIM*i)*scale);
                int curY = (int)Math.round((Y_OFFSET+Y_DIM*j)*scale);
                int nextX = (int)Math.round((X_OFFSET+X_DIM*(i+1))*scale);
                int nextY = (int)Math.round((Y_OFFSET+Y_DIM*(j+1))*scale);
                int deltaX = nextX-curX; 
                int deltaY = nextY-curY;

                g.fillRect( curX, curY, deltaX, deltaY );
                Color curColour = this.grid[i][j];
                if (curColour != null) // Draw pegs if they exist
                {
                    g.setColor(curColour);
                    g.fillOval(curX+deltaX/4, curY+deltaY/4, deltaX/2, deltaY/2);
                }
            }
        }
        ((Graphics2D) g).setStroke( new BasicStroke(0.5f) );
        g.setColor(Color.BLACK);
        int curX = (int)Math.round(X_OFFSET*scale);
        int curY = (int)Math.round(Y_OFFSET*scale);
        int nextX = (int)Math.round((X_OFFSET+X_DIM*grid.length)*scale);
        int nextY = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale);
        g.drawRect(curX, curY, nextX-curX, nextY-curY);
    }

    private void drawLine(Graphics g)
    {
        for (int i =0; i < numLines; i++ ) 
        {
            ((Graphics2D) g).setStroke( new BasicStroke( 5.0f*(float)scale) );
            g.drawLine( (int)Math.round((X_OFFSET+X_DIM/2.0+line[0][i]*X_DIM)*scale), 
                    (int)Math.round((Y_OFFSET+Y_DIM/2.0+line[1][i]*Y_DIM)*scale), 
                    (int)Math.round((X_OFFSET+X_DIM/2.0+line[2][i]*X_DIM)*scale), 
                    (int)Math.round((Y_OFFSET+Y_DIM/2.0+line[3][i]*Y_DIM)*scale) );
        }
    }

    /**
     * Convert a String to the corresponding Color defaulting to Black 
     * with an invald input
     */
    /*private Color convertColour( String theColour )
    {
        for( int i=0; i<COLOUR_NAMES.length; i++ )
        {
            if( COLOUR_NAMES[i].equalsIgnoreCase( theColour ) )
                return COLOURS[i];
        }

        return DEFAULT_COLOUR;
    }*/


    /** The method that draws everything
     */
    public void paintComponent( Graphics g ) 
    {
        this.setScale();
        this.paintGrid(g);
        this.drawLine(g);
        this.paintText(g);
    }

    public void setScale()
    {
        double width = (0.0+this.getSize().width) / this.originalWidth;
        double height = (0.0+this.getSize().height) / this.originalHeight;
        this.scale = Math.max( Math.min(width,height), MIN_SCALE ); 
    }

    /** Sets the message to be displayed under the board
     */
    public void displayMessage(String theMessage)
    {
        message = theMessage;
        this.repaint();
    }


    /** This method will save the value of the colour of the peg in a specific 
     * spot.  theColour is restricted to 
     *   "yellow", "blue", "cyan", "green", "pink", "white", "red", "orange"  
     * Otherwise the colour black will be used. 
     */
    public void putPeg(Color colour, int row, int col)
    {
        this.grid[col][row] = colour;
        this.repaint();
    }

    /** Same as putPeg above but for 1D boards
     */
    public void putPeg(Color colour, int col)
    {
        this.putPeg(colour, 0, col );
    }

    /** Remove a peg from the gameboard.
     */
    public void removePeg(int row, int col)
    {
        this.grid[col][row] = null;
        repaint();
    }

    /** Same as removePeg above but for 1D boards
     */
    public void removePeg(int col)
    {
        this.grid[col][0] = null;
        repaint();
    }

    /** Draws a line on the board using the given co-ordinates as endpoints
     */
    public void drawLine(double row1, double col1, double row2, double col2)
    {
        this.line[0][numLines]=col1;
        this.line[1][numLines]=row1;
        this.line[2][numLines]=col2;
        this.line[3][numLines]=row2;
        this.numLines++;
        repaint();
    }

    /** Removes one line from a board given the co-ordinates as endpoints
     * If there is no such line, nothing happens
     * If multiple lines, all copies are removed
     */

    public void removeLine(int row1, int col1, int row2, int col2) 
    {
        int curLine = 0;
        while (curLine < this.numLines) 
        {
            // Check for either endpoint being specified first in our line table
            if ( (line[0][curLine] == col1 && line[1][curLine] == row1 &&
                    line[2][curLine] == col2 && line[3][curLine] == row2)   || 
                    (line[2][curLine] == col1 && line[3][curLine] == row1 &&
                    line[0][curLine] == col2 && line[1][curLine] == row2) )
            {
                // found a matching line: overwrite with the last one
                numLines--;
                line[0][curLine] = line[0][numLines];
                line[1][curLine] = line[1][numLines];
                line[2][curLine] = line[2][numLines];
                line[3][curLine] = line[3][numLines];
                curLine--; // perhaps the one we copied is also a match
            }
            curLine++;

        }
        repaint();
    }

    /** Waits for user to click somewhere and then returns the click.
     */
    public Point getClick()
    {
        Point returnedClick = null;
        synchronized(this) {
            lastClick = null;
            while (lastClick == null)
            {
                try {
                    this.wait();
                } catch(Exception e) {
                    // We'll never call Thread.interrupt(), so just consider
                    // this an error.
                    e.printStackTrace();
                    System.exit(-1) ;
                } /* try */
            }

            int x = (int)Math.floor((lastClick.getY()-X_OFFSET*scale)/X_DIM/scale);
            int y = (int)Math.floor((lastClick.getX()-Y_OFFSET*scale)/Y_DIM/scale);

            // Put this into a new object to avoid a possible race.
            returnedClick = new Point(x,y);
        }
        return returnedClick;
    }

    /** Same as getClick above but for 1D boards
     */
    public double getPosition()
    {
        return this.getClick().getY();
    }

    public int getColumns()
    {
        return this.columns;
    }

    public int getRows()
    {
        return this.rows;
    }
}

你用线程代码搬起石头砸自己的脚——你调用的是 运行() 而不是 start() 就可以了

SYNC_BOARD.run();

这将 运行 在 Swing 事件线程上并有完全冻结您的 GUI 的风险。

作为一般规则,您几乎不应该扩展 Thread,而是实现 Runnable,但无论如何,不​​要使用 Thread 代码——而是使用 Swing Timer,因为您的代码没有中断,没有 Thread.sleeps 并且它会让您的 CPU 非常忙碌,而 Swing 计时器将帮助确保您的代码遵守 Swing 线程规则。

你的MoveAction也是错误的。大多数代码应该放在 actionPerformed 方法中。构造函数应该只设置方向字段,仅此而已。

类似于:

@SuppressWarnings("serial")
public class MoveAction extends AbstractAction {
    Direction direction;

    public MoveAction(Direction direction) {
        // this is the only code the constructor should have!
        this.direction = direction;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // use direction to help make move in here
    }
}

了解这可能会导致一些重大问题,因为在程序创建时会调用构造函数(因此在程序启动时会调用键绑定 "work"),但在正确的时候实际调用的是 actionPerformed键被按下。