简体   繁体   中英

Java key releases not being picked up?

I'm new to java programming and have begun programming a game. I have a character that moves with wasd but found that when:

  1. A movement key is pressed.
  2. The JFrame clicked away from. (So it becomes unfocused.)
  3. The same key is released.
  4. JFrame is focused again.

...that the game won't pick it up that the player shouldn't be moving. So I tried to fix it by changing all the keys array to false:

public class KeyManager implements KeyListener {

private Game game;

private boolean[] keys;
public boolean up, down, left, right; //Player class reads these variables

public KeyManager(Game game) {
    this.game = game;
    keys = new boolean[256];
}

public void tick() {
    if(!game.getDisplay().getFrame().isFocused()) { //Here is the problem
        for(int i = 0; i < keys.length; i++) {
            keys[i] = false; //Repeats 256 x 60 times a second
        }
    }
    up = keys[KeyEvent.VK_W];    //I realize that I could just change
    down = keys[KeyEvent.VK_S];  //up, down, left, right to false but
    left = keys[KeyEvent.VK_A];  //when the JFrame is refocused the game
    right = keys[KeyEvent.VK_D]; //still doesn't know the key was released
}

@Override
public void keyPressed(KeyEvent e) {
    keys[e.getKeyCode()] = true;
}

@Override
public void keyReleased(KeyEvent e) {
    keys[e.getKeyCode()] = false;
}

@Override
public void keyTyped(KeyEvent e) {

}

}

It worked, but after thinking over the code I realized that since the tick() method is called 60 times a second and the keys array is 256 keys long that it was updating 15,900 key booleans every second for only 4 important keys that needed to be changed to false.

What is the most efficient way to do this? I feel like I'm missing something really simple.

The first thing I would do, is use the Key Bindings API instead of a KeyListener . I would have it update the key flags (personally, I'd use a Set and some type of Input enum , but that's me) independently of the "game loop".

I would then use a FocusListener on the JFrame and simply pause/resume the "game loop" when focus is lost/gained, optionally resetting the key flags

Something like this, for example...

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.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

    public Test() {
        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");
                TestPane tp = new TestPane();
                frame.addFocusListener(new FocusListener() {
                    @Override
                    public void focusGained(FocusEvent e) {
                        tp.resume();
                    }

                    @Override
                    public void focusLost(FocusEvent e) {
                        tp.pause(true);
                    }
                });
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(tp);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private boolean aPressed = false;
        private Timer timer;

        public TestPane() {
            InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap actionMap = getActionMap();

            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "A.pressed");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "A.released");
            actionMap.put("A.pressed", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    aPressed = true;
                }
            });
            actionMap.put("A.released", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    aPressed = false;
                }
            });

            timer = new Timer(16, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println("A pressed = " + aPressed);
                }
            });
        }

        public void resume() {
            timer.restart();
        }

        public void pause(boolean reset) {
            timer.stop();
            if (reset) {
                aPressed = false;
            }
        }

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

    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM