Метод KeyListener keyPressed не работает - PullRequest
0 голосов
/ 25 апреля 2020

Я пытаюсь сделать небольшую игру, используя java swing, и продвинулся довольно далеко, но теперь мой KeyListener не использует метод keyPressed.

Вот часть моего кода

public class Screen extends JPanel implements Runnable{

private static final int WIDTH = 300, HEIGHT = 300, RIGHT = 0, LEFT = 1, UP = 2, DOWN = 3, STILL = 4;
private Thread thread;
private boolean running = false;
private int direction = DOWN;

   public Screen() {
    setFocusable(true);
    addKeyListener(new Key());
    setPreferredSize(new Dimension(WIDTH, HEIGHT));
    start();
   }

   public void tick(){
     System.out.println(direction)  
   }

   public void start() {
    running = true;
    thread = new Thread(this, "Game Loop");
    thread.start();
}

   public void run() {
     while (running) {
       tick();
       repaint();
    }
}

   private class Key implements KeyListener{
    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();

        if (key == KeyEvent.VK_D) {
            direction = RIGHT;
        }

        if (key == KeyEvent.VK_A) {
            direction = LEFT;
        }

        if (key == KeyEvent.VK_W) {
            direction = UP;
        }

        if (key == KeyEvent.VK_S) {
            direction = DOWN;
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub

    }
   }
}

Я смотрю на консоль и ожидаю, что выходной сигнал изменится с 3 (что ВНИЗ) на любую кнопку, которую я нажимаю, но она этого не делает. Через некоторое время я понял, что что-то не так с методом keyPressed, я просто не знаю, что.

Ответы [ 2 ]

0 голосов
/ 26 апреля 2020

Используйте API связывания клавиш , он решит эту и все другие KeyListener проблемы без большого количества взлома и царапин из-за того, почему он "иногда" работает

Качели однопоточные. Это означает, что вы должны быть осторожны при обновлении состояния пользовательского интерфейса (прямо или косвенно) вне контекста потока диспетчеризации событий, это также включает любое состояние, от которого пользовательский интерфейс может зависеть.

Посмотрите на Параллелизм в Swing для получения более подробной информации.

Без подробностей одним из самых простых решений было бы использование Swing Timer в качестве основного "тикового" действия. Каждый раз, когда он «тикает», вы проверяете состояние входа, обновляете состояние модели и запускаете перерисовку для обновления пользовательского интерфейса

Например ...

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
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;

public class Main {

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

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

    public static class TestPane extends JPanel {

        enum Input {
            UP, DOWN, LEFT, RIGHT
        }

        private Set<Input> input = new HashSet<>();

        public TestPane() {
            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Left.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Left.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Right.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Right.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Up.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Up.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Down.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Down.released");

            am.put("Left.pressed", new InputAction(Input.LEFT, input, false));
            am.put("Left.released", new InputAction(Input.LEFT, input, true));
            am.put("Right.pressed", new InputAction(Input.RIGHT, input, false));
            am.put("Right.released", new InputAction(Input.RIGHT, input, true));
            am.put("Down.pressed", new InputAction(Input.DOWN, input, false));
            am.put("Down.released", new InputAction(Input.DOWN, input, true));
            am.put("Up.pressed", new InputAction(Input.UP, input, false));
            am.put("Up.released", new InputAction(Input.UP, input, true));

            input.add(Input.UP);
            input.add(Input.DOWN);
            input.add(Input.LEFT);
            input.add(Input.RIGHT);

            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // Check what's currently been pressed
                    // and update the state accordingly...
                    if (input.contains(Input.UP)) {
                        //..
                    }
                    if (input.contains(Input.DOWN)) {
                        //..
                    }
                    if (input.contains(Input.LEFT)) {
                        //..
                    }
                    if (input.contains(Input.RIGHT)) {
                        //..
                    }
                    repaint();
                }
            });
            timer.start();
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            int midX = getWidth() / 2;
            int midY = getHeight() / 2;

            FontMetrics fm = g2d.getFontMetrics();
            int spaceWidth = fm.stringWidth("M");
            int spaceHeight = fm.getHeight();

            if (input.contains(Input.UP)) {
                String text = "UP";
                g2d.drawString(text,
                        midX - (fm.stringWidth(text)) / 2,
                        (midY - (spaceHeight * 2) + fm.getAscent()));
            }
            if (input.contains(Input.DOWN)) {
                String text = "DOWN";
                g2d.drawString(text,
                        midX - (fm.stringWidth(text)) / 2,
                        (midY + spaceHeight + fm.getAscent()));
            }
            if (input.contains(Input.LEFT)) {
                String text = "LEFT";
                g2d.drawString(text,
                        (midX - spaceWidth - fm.stringWidth(text)),
                        (midY + (fm.getAscent() / 2)));
            }
            if (input.contains(Input.RIGHT)) {
                String text = "RIGHT";
                g2d.drawString(text,
                        (midX + spaceWidth),
                        (midY + (fm.getAscent() / 2)));
            }
            g2d.dispose();
        }

        public class InputAction extends AbstractAction {

            private Input input;
            private Set<Input> inputSet;
            private boolean onRelease;

            public InputAction(Input input, Set<Input> inputSet, boolean onRelease) {
                this.input = input;
                this.inputSet = inputSet;
                this.onRelease = onRelease;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                if (onRelease) {
                    inputSet.remove(input);
                } else {
                    inputSet.add(input);
                }
            }

        }

    }
}

Это базовый пример c, который печатает состояние клавишных вводов непосредственно в методе paintComponent. Более реалистичная реализация c будет использовать Timer ActionListener для проверки состояния input и внесения соответствующих изменений в требуемую модель, на что намекает пример.

Если вы требует более непосредственного контроля, тогда вам нужно избегать системы окраски Swing и взять контроль над собой. Для этого вам нужно будет использовать BufferStrategy. Это более сложное решение, но оно также более мощное, поскольку вы получаете полный контроль над обновлением пользовательского интерфейса

0 голосов
/ 25 апреля 2020

Вам не нужна отдельная тема "Game L oop". Swing управляется событиями. Ваш ключевой код слушателя будет вызван, когда пользователь нажмет клавишу на клавиатуре компьютера.

Как MadProgrammer , упомянутый в его комментарии к вашему вопросу

изменение состояния пользовательского интерфейса вне контекста EDT может вызвать проблемы с рисованием

Другими словами, Swing является однопоточным и этот поток называется потоком отправки событий (EDT), и весь ваш код, изменяющий GUI, должен выполняться только на EDT.

Приведенный ниже код является подставкой - Swing приложение, которое отображает фокусируемое JPanel. Когда вы нажимаете одну из соответствующих клавиш «направления» клавиатуры, то есть A или D или S или W, консоль отображает направление. Нажатие любой другой клавиши вызывает направление STILL на консоль. Обратите внимание, что я использовал enum для различных направлений, а не целочисленные константы.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

public class Screen01 extends JPanel implements Runnable {
    public enum Directions {RIGHT, LEFT, UP, DOWN, STILL};

    private static final int WIDTH = 300, HEIGHT = 300;
    private Directions direction;

    public Screen01() {
        setPreferredSize(new Dimension(WIDTH, HEIGHT));
        setFocusable(true);
        addKeyListener(new Key());
    }

    public void run() {
        createAndDisplayGui();
    }

    private void createAndDisplayGui() {
        JFrame frame = new JFrame("Screen");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(this, BorderLayout.CENTER);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private class Key implements KeyListener {

        @Override
        public void keyTyped(KeyEvent e) {
            // Do nothing.
        }

        @Override
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            switch (key) {
                case KeyEvent.VK_D:
                    direction = Directions.RIGHT;
                    break;
                case KeyEvent.VK_A:
                    direction = Directions.LEFT;
                    break;
                case KeyEvent.VK_W:
                    direction = Directions.UP;
                    break;
                case KeyEvent.VK_S:
                    direction = Directions.DOWN;
                    break;
                default:
                    direction = Directions.STILL;
            }
            System.out.println("Direction = " + direction);
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // Do nothing.
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Screen01());
    }
}
...