首页 文章

Java Key Bindings的触发非常不一致

提问于
浏览
0

我正在制作一款游戏,其中包含一个我想用WASD移动的玩家 . 我已经决定使用Key Bindings来尝试修复我使用Key Listeners时出现的问题,即使在切换到Key Bindings后,它仍然会发生 . 问题是,即使按下这些键正在移动播放器,在移动播放器几次后,输入开始几乎完全停止工作 . 这恰好可能只有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

    关于“为什么”你有问题,我只能猜测,因为我们只有一个脱离上下文的片段来处理,但是,有许多事情可以改进,这可能有助于解决问题 .

    调用 getInputMap

    • 用户 WHEN_IN_FOCUSED_WINDOW

    这适用于实际触发键事件的上下文,在上述情况下,当窗口具有焦点时将触发键事件,而不管当前哪个组件具有键盘焦点 .

    • 首选覆盖 paintComponent over paint

    在Swing中绘画是一些复杂的,如一般建议,当你想要执行自定义绘画时,最好覆盖 paintComponent . 这样做的一个很好的副作用是,它会为你的组件绘制背景颜色,你需要做的一件事;)

    • 当您需要提供尺寸提示时,请首选覆盖 getPreferredSize

    覆盖 getWidthgetHeight 将导致可能的问题无法结束,最好避免 . 相反,覆盖 getPreferredSize ,这样,您将参与布局API并获得其所有优势,例如能够在 JFrame 上调用 pack 并让它处理围绕框架装饰的所有奇怪事项 .

    • 从用于影响该状态的机制中分离状态的变化

    很好的罗嗦“解耦你的代码” . 在您的代码中,玩家的状态由关键操作直接更改 . 这不仅是一个坏主意,它可能产生意想不到的副作用,并且随着需求变得更加复杂而变得越来越难以管理 . 它还使得改变输入的方式变得困难 . 例如,您可以包含不同的输入方法,例如游戏杆,但您必须为其编写相同的代码 .

    相反,你应该只有一个“状态管理器”,它带有输入的当前状态,当你的“主循环”准备就绪时,它会将该状态应用于模型,作为一个单独的步骤 .

    “主循环”并不关心状态如何更新,只是它可以获得决定如何应用它所需的信息 .

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

    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);
                }
            });
        }
    }
    

    我应该指出,这只是一种方法 . 也可以把一堆"flags"放在某种 Set

相关问题