Java键绑定的触发非常不一致

时间:2018-12-08 23:13:02

标签: java swing jpanel key-bindings keyevent

我正在开发一个游戏,该游戏的玩家希望使用WASD进行移动。我决定使用键绑定,以尝试解决我与键侦听器有关的问题,即使切换到键绑定后,该问题现在仍在发生。问题是,即使按下这些键正在移动播放器,但在移动播放器几次之后,输入几乎完全停止工作。发生这种情况时,可能只有1/10键按下才能移动播放器。我可能做错了什么?任何帮助将不胜感激。

这是我游戏的主要绑定类: (让我知道是否应发布其余代码)

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.Color;  
import java.awt.Graphics;  
import javax.swing.JComponent;
import java.lang.Math;
import java.util.LinkedList;
import java.awt.event.KeyEvent;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.KeyStroke;

public class ZombieMain extends JPanel implements ActionListener{

   private static int WIDTH = 1600;
   private static int HEIGHT = 900;

   private Action a,s,w,d,ra,rs,rw,rd;

   private LinkedList<Zombie> zombies = new LinkedList();
   Zombie z = new Zombie(100,100,50,30,50);
   static ZombiePlayer player = new ZombiePlayer(950,572,2,30);

   public ZombieMain(){
      this.setFocusable(true);
      this.requestFocus();

      Timer t = new Timer(10,this);
      t.start();

      Action w = new AbstractAction() {
         public void actionPerformed(ActionEvent e) {
            player.vY = -player.speed;
         }
      };
      Action s = new AbstractAction() {
         public void actionPerformed(ActionEvent e) {
            player.vY = player.speed;
         }
      };
      Action d = new AbstractAction() {
         public void actionPerformed(ActionEvent e) {
            player.vX = player.speed;
         }
      };
      Action a = new AbstractAction() {
         public void actionPerformed(ActionEvent e) {
            player.vX = -player.speed;
         }
      };
      Action rw = new AbstractAction() {
         public void actionPerformed(ActionEvent e) {
            player.vY = 0;
         }
      };
      Action rs = new AbstractAction() {
         public void actionPerformed(ActionEvent e) {
            player.vY = 0;
         }
      };
      Action rd = new AbstractAction() {
         public void actionPerformed(ActionEvent e) {
            player.vX = 0;
         }
      };
      Action ra = new AbstractAction() {
         public void actionPerformed(ActionEvent e) {
            player.vX = 0;
         }
      };

     getInputMap().put(KeyStroke.getKeyStroke("W"),"w");
     getInputMap().put(KeyStroke.getKeyStroke("S"),"s");
     getInputMap().put(KeyStroke.getKeyStroke("D"),"d");
     getInputMap().put(KeyStroke.getKeyStroke("A"),"a");
     getInputMap().put(KeyStroke.getKeyStroke("released W"),"rw");
     getInputMap().put(KeyStroke.getKeyStroke("released S"),"rs");
     getInputMap().put(KeyStroke.getKeyStroke("released D"),"rd");
     getInputMap().put(KeyStroke.getKeyStroke("released A"),"ra");

     getActionMap().put("w",w);
     getActionMap().put("s",s);
     getActionMap().put("d",d);
     getActionMap().put("a",a);
     getActionMap().put("rw",rw);
     getActionMap().put("rs",rs);
     getActionMap().put("rd",rd);
     getActionMap().put("ra",ra);
   }

   public void actionPerformed(ActionEvent e){
      repaint();
   }

   public void paint(Graphics g){
      g.setColor(new Color(40,40,40));
      g.fillRect(0,0,WIDTH,HEIGHT);
      z.draw((Graphics)g);
      player.draw((Graphics)g);
   }

   public int getWidth(){
      return WIDTH;
   }
   public int getHeight(){
      return HEIGHT;
   }
   public static void main(String[] args){
        JFrame frame = new JFrame();
        frame.add(new ZombieMain());
        frame.setSize(WIDTH,HEIGHT);  
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
        frame.setVisible(true);
        frame.setResizable(false);
   }
}

1 个答案:

答案 0 :(得分:1)

关于“为什么”您遇到问题,我只能猜测,因为我们只有一个上下文外代码段可以处理,但是,有很多事情可以改进,这可能会有所帮助解决问题。

  • 用户WHEN_IN_FOCUSED_WINDOW在致电getInputMap

这将上下文应用于实际触发按键事件的时间,在上述情况下,无论窗口哪个具有焦点,无论当前哪个组件具有键盘焦点,按键事件都会被触发。

  • paintComponent上优先覆盖paint

Swing中的绘画有些复杂,作为一般建议,当您要执行自定义绘画时,最好覆盖paintComponent。一个不错的副作用是,它将为您绘制组件的背景颜色,这是您要做的一件事;)

  • 需要提供大小调整提示时,首选覆盖getPreferredSize

覆盖getWidthgetHeight不会引起任何可能的问题,最好避免。相反,以这种方式覆盖getPreferredSize,您将参与布局API并获得其所有优点,例如能够在pack上调用JFrame并使其得到照顾奇怪的是围绕着框架装饰。

  • 将状态变化与影响状态的机制分开

好意思的“解耦代码”。在您的代码中,播放器的状态可以通过按键操作直接更改。这不仅是一个坏主意,它还会产生意想不到的副作用,并且随着需求变得越来越复杂,变得越来越难以管理。这也使得很难改变输入的方式。例如,您可以包括不同的输入法,例如操纵杆,但是您必须为其编写相同的代码。

相反,您应该简单地使用一个“状态管理器”来承载输入的当前状态,当您的“主循环”准备就绪时,它将作为单独的步骤将该状态应用于模型。

“主循环”不在乎状态如何更新,只是它可以获取决定如何应用状态所需的信息。

下面是我上面讨论的所有内容的一个粗略示例,在测试中,绑定“ stalling”没有问题

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
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 ZombieMain extends JPanel implements ActionListener {

    enum VerticalDirection {
        UP, DOWN, NONE
    }

    enum HorizontalDirection {
        LEFT, RIGHT, NONE
    }

    public class VerticalStateController {

        private VerticalDirection state = VerticalDirection.NONE;

        public void setState(VerticalDirection state) {
            this.state = state;
        }

        public VerticalDirection getState() {
            return state;
        }

    }

    public class HorizontalStateController {

        private HorizontalDirection state = HorizontalDirection.NONE;

        public void setState(HorizontalDirection state) {
            this.state = state;
        }

        public HorizontalDirection getState() {
            return state;
        }

    }

    public class VerticalAction extends AbstractAction {

        private VerticalStateController controller;
        private VerticalDirection state;

        public VerticalAction(VerticalStateController controller, VerticalDirection state) {
            this.controller = controller;
            this.state = state;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            controller.setState(state);
        }

    }

    public class HorizontalAction extends AbstractAction {

        private HorizontalStateController controller;
        private HorizontalDirection state;

        public HorizontalAction(HorizontalStateController controller, HorizontalDirection state) {
            this.controller = controller;
            this.state = state;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            controller.setState(state);
        }

    }

    private static int WIDTH = 400;
    private static int HEIGHT = 400;

    private Rectangle player = new Rectangle(0, 0, 20, 20);
    private VerticalStateController verticalStateController = new VerticalStateController();
    private HorizontalStateController horizontalStateController = new HorizontalStateController();

    public ZombieMain() {
        setBackground(new Color(40, 40, 40));

        Timer t = new Timer(10, this);
        t.start();

        InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
        ActionMap am = getActionMap();

        // Pressed
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Pressed.up");
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Pressed.down");
        // Released
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Released.up");
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Released.down");

        // Pressed
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Pressed.left");
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Pressed.right");
        // Released
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Released.left");
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Released.right");

        am.put("Pressed.up", new VerticalAction(verticalStateController, VerticalDirection.UP));
        am.put("Pressed.down", new VerticalAction(verticalStateController, VerticalDirection.DOWN));
        am.put("Released.up", new VerticalAction(verticalStateController, VerticalDirection.NONE));
        am.put("Released.down", new VerticalAction(verticalStateController, VerticalDirection.NONE));

        am.put("Pressed.left", new HorizontalAction(horizontalStateController, HorizontalDirection.LEFT));
        am.put("Pressed.right", new HorizontalAction(horizontalStateController, HorizontalDirection.RIGHT));
        am.put("Released.left", new HorizontalAction(horizontalStateController, HorizontalDirection.NONE));
        am.put("Released.right", new HorizontalAction(horizontalStateController, HorizontalDirection.NONE));
    }

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

    public void actionPerformed(ActionEvent e) {
        switch (verticalStateController.getState()) {
            case UP:
                player.y -= 4;
                break;
            case DOWN:
                player.y += 4;
                break;
        }
        switch (horizontalStateController.getState()) {
            case LEFT:
                player.x -= 4;
                break;
            case RIGHT:
                player.x += 4;
                break;
        }
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setColor(Color.RED);
        g2d.fill(player);
        g2d.dispose();
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new ZombieMain());
                frame.pack();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

我应该指出,这只是一种方法。也可以在某种Set中放置一堆“标志”