Certain key combinations don't work in a Java

2019-09-02 03:00发布

问题:

EDIT: Added code.

In a 2D Java game, I have two tanks on the screen. Their controls:

Tank 1

  • Move forward: UP arrow key
  • Adjust angle of movement (and rotate sprite): LEFT arrow and RIGHT arrow.
  • Shoot missile: ENTER key.

Tank 2

  • Move forward: W key
  • Adjust angle of movement (and rotate sprite): A key and D key.
  • Shoot missile: G key.

The program uses Key Bindings to do this.

I have a wierd bug:

Pressing W, D, and G at the same time - works. (aka: rotating the sprite to the right and adjusting the movement angle, while moving in that angle and shooting missiles at the same time).

But doing that exact same thing, except instead of D, I press A - doesn't work. The missiles don't shoot. (Aka: W + D + G = works. But W + A + G = doesn't work. The missiles don't happen).

However, this problem doesn't happen with the other tank. The other tank can do UP + LEFT + ENTER at the same time (equivalent of W + A + G for the first tank).

I need this game to work fluidly on any standard keyboard, because it's a 2-player game where precision and timing is important.

Is there a problem with my code?

Thanks

KBThread class - the class the does the key-bindings.

When a key is pressed, a flag inside an array is set (true). When it's released, the flag is set to false.

Another class (the one with most of the game), checks what keys are pressed by checking their flags, and does things accordingly.

public class KBThread implements Runnable {

    Board b;

    Action upAction,leftAction,rightAction,wAction,aAction,dAction,upReleased,leftReleased,rightReleased,wReleased,dReleased,aReleased,
                enterAction, gAction;
    InputMap inputMap;
    ActionMap actionMap;

    boolean[] keysPressed1,keysPressed2;
    ArrayList missiles1,missiles2;

    Tank tank1,tank2;

    public KBThread(Board b){
        this.b = b;
        inputMap = b.getInputMap();
        actionMap = b.getActionMap();
        tank1 = b.tank1;
        tank2 = b.tank2;
        keysPressed1 = tank1.getKeys();
        keysPressed2 = tank2.getKeys();
        missiles1 = tank1.getMissiles();
        missiles2 = tank2.getMissiles();
    }

    public void run() {


        // Keys pressed

        leftAction = new AbstractAction(){
            public void actionPerformed(ActionEvent e){     
                keysPressed1[0] = true;
            }
        };

        rightAction = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed1[1] = true;
            }
        };

        upAction = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed1[2] = true;
            }
        };

        aAction = new AbstractAction(){
            public void actionPerformed(ActionEvent e){ 
                keysPressed2[0] = true;
            }
        };

        dAction = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed2[1] = true;
            }
        };

        wAction = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed2[2] = true;
            }
        };

        enterAction = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                missiles1.add( new Missile( tank1.getX(), tank1.getY(), "blue" , tank1.getAngle() , tank1) );
            }
        };

        gAction = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                missiles2.add( new Missile( tank2.getX(), tank2.getY(), "red", tank2.getAngle() , tank2) );
            }
        };

        // Keys released

        leftReleased = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed1[0] = false;
            }
        };

        rightReleased = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed1[1] = false;
            }
        };

        upReleased = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed1[2] = false;
            }
        };

        aReleased = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed2[0] = false;
            }
        };

        dReleased = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed2[1] = false;
            }
        };

        wReleased = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                keysPressed2[2] = false;
            }
        };

        inputMap.put(KeyStroke.getKeyStroke("UP"),"upAction");
        inputMap.put(KeyStroke.getKeyStroke("LEFT"),"leftAction");
        inputMap.put(KeyStroke.getKeyStroke("RIGHT"),"rightAction");
        inputMap.put(KeyStroke.getKeyStroke("W"),"wAction");
        inputMap.put(KeyStroke.getKeyStroke("A"),"aAction");
        inputMap.put(KeyStroke.getKeyStroke("D"),"dAction");
        inputMap.put(KeyStroke.getKeyStroke("ENTER"),"enterAction");
        inputMap.put(KeyStroke.getKeyStroke("G"),"gAction");

        actionMap.put("upAction",upAction);
        actionMap.put("leftAction",leftAction);
        actionMap.put("rightAction",rightAction);
        actionMap.put("wAction",wAction);
        actionMap.put("aAction",aAction);
        actionMap.put("dAction",dAction);
        actionMap.put("enterAction",enterAction);
        actionMap.put("gAction",gAction);

        inputMap.put(KeyStroke.getKeyStroke("released UP"),"upReleased");
        inputMap.put(KeyStroke.getKeyStroke("released LEFT"),"leftReleased");
        inputMap.put(KeyStroke.getKeyStroke("released RIGHT"),"rightReleased");
        inputMap.put(KeyStroke.getKeyStroke("released W"),"wReleased");
        inputMap.put(KeyStroke.getKeyStroke("released A"),"aReleased");
        inputMap.put(KeyStroke.getKeyStroke("released D"),"dReleased");

        actionMap.put("upReleased",upReleased);
        actionMap.put("leftReleased",leftReleased);
        actionMap.put("rightReleased",rightReleased);
        actionMap.put("wReleased",wReleased);
        actionMap.put("aReleased",aReleased);
        actionMap.put("dReleased",dReleased);

    }

}

Part of the game-loop. This manages moving sprites and shooting missiles.

                keysPressed1 = tank1.getKeys();
                keysPressed2 = tank2.getKeys();

                if(keysPressed1[0]==true)tank1.setAngle(tank1.getAngle()-3);
                if(keysPressed1[1]==true)tank1.setAngle(tank1.getAngle()+3);
                if(keysPressed1[2]==true){
                    tank1.setDX(2 * Math.cos(Math.toRadians(tank1.getAngle())));
                    tank1.setDY(2 * Math.sin(Math.toRadians(tank1.getAngle())));
                }

                if(keysPressed1[2]==false){
                    tank1.setDX(0);
                    tank1.setDY(0);
                }

                tank1.move();

                for(int i=0;i<missiles1.size();i++){
                    m = (Missile) missiles1.get(i);
                    m.move();
                }


                if(keysPressed2[0]==true)tank2.setAngle(tank2.getAngle()-3);
                if(keysPressed2[1]==true)tank2.setAngle(tank2.getAngle()+3);
                if(keysPressed2[2]==true){
                    tank2.setDX( (-1) * ( 2 * Math.cos(Math.toRadians(tank2.getAngle()) ) ));
                    tank2.setDY( (-1) * (2 * Math.sin(Math.toRadians(tank2.getAngle()) ) ));
                }

                if(keysPressed2[2]==false){
                    tank2.setDX(0);
                    tank2.setDY(0);
                }

                tank2.move();

                for(int i=0;i<missiles2.size();i++){
                    m = (Missile) missiles2.get(i);
                    m.move();
                }

回答1:

I wrote a simple test and have no issues. Are you able to replicate your issues with this?

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
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.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestKeyBindings {

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

    public TestKeyBindings() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private final List<String> keys;
        private final JLabel lblKeys = new JLabel("<nothing>");

        public TestPane() {
            keys = new ArrayList<>(3);
            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "W-Down");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "D-Down");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, false), "G-Down");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "A-Down");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "W-Up");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "D-Up");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, true), "G-Up");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "A-Up");

            ActionMap am = getActionMap();
            am.put("W-Down", new KeyDownAction("W"));
            am.put("D-Down", new KeyDownAction("D"));
            am.put("G-Down", new KeyDownAction("G"));
            am.put("A-Down", new KeyDownAction("A"));
            am.put("W-Up", new KeyUpAction("W"));
            am.put("D-Up", new KeyUpAction("D"));
            am.put("G-Up", new KeyUpAction("G"));
            am.put("A-Up", new KeyUpAction("A"));

            setLayout(new GridBagLayout());
            add(lblKeys);
        }

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

        protected void update() {
            StringBuilder sb = new StringBuilder(128);
            if (keys.isEmpty()) {
                sb.append("<nothing>");
            } else {
                for (String key : keys) {
                    if (sb.length() > 0) {
                        sb.append("+");
                    }
                    sb.append(key);
                }
            }
            lblKeys.setText(sb.toString());
        }

        public void addKey(String key) {
            if (!keys.contains(key)) {
                keys.add(key);
                update();
            }
        }

        public void removeKey(String key) {
            keys.remove(key);
            update();
        }

        public class KeyDownAction extends AbstractAction {

            private final String key;

            public KeyDownAction(String key) {
                this.key = key;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                addKey(key);
            }

        }

        public class KeyUpAction extends AbstractAction {

            private final String key;

            public KeyUpAction(String key) {
                this.key = key;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                removeKey(key);
            }

        }

    }

}