Java键盘输入-游戏开发

Java键盘输入-游戏开发,java,swing,awt,keylistener,key-bindings,Java,Swing,Awt,Keylistener,Key Bindings,我为班级制作的游戏有一个特殊的“问题” 这个游戏是“打破它”的一个实现。为了移动底部的平台,我只使用了一个键侦听器。问题是,在第一次按键后,在平台开始移动之前会出现短暂的“滞后”或“结巴”。我如何防止这种情况发生,以获得平稳的响应?除了KeyListener还有别的方法吗?键绑定 下面是关键侦听器实现 private class KeyControl implements KeyListener { private int dx = 20; public void keyPr

我为班级制作的游戏有一个特殊的“问题”

这个游戏是“打破它”的一个实现。为了移动底部的平台,我只使用了一个键侦听器。问题是,在第一次按键后,在平台开始移动之前会出现短暂的“滞后”或“结巴”。我如何防止这种情况发生,以获得平稳的响应?除了KeyListener还有别的方法吗?键绑定

下面是关键侦听器实现

private class KeyControl implements KeyListener {

    private int dx = 20;

    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
            if(dx < 0 )
                dx = -dx;
            gamePanel.movePlatform(dx);
        }

        if(e.getKeyCode() == KeyEvent.VK_LEFT) {
            if(dx > 0 )
                dx = -dx;
            gamePanel.movePlatform(dx);
        }

        if(e.getKeyCode() == KeyEvent.VK_SPACE) {
            System.out.println("space");
            gamePanel.play();
        }

        if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
            gamePanel.pause();
        }

    }
}
私有类KeyControl实现KeyListener{
私有整数dx=20;
按下公共无效键(按键事件e){
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
if(dx<0)
dx=-dx;
gamePanel.movePlatform(dx);
}
如果(例如getKeyCode()==KeyEvent.VK_左){
如果(dx>0)
dx=-dx;
gamePanel.movePlatform(dx);
}
if(例如getKeyCode()==KeyEvent.VK_空间){
System.out.println(“空间”);
gamePanel.play();
}
if(例如getKeyCode()==KeyEvent.VK_ESCAPE){
gamePanel.pause();
}
}
}
这是移动平台的方法

public void movePlatform(int dx) {
        int nextDX = dx;

        if(paused || init) {
            dx = 0;
        }

        // make sure platform doesnt exceed right border
        if(platform.getX() + platform.getWidth()  + dx> size.getWidth()) {
            if(nextDX < 0)
                dx = nextDX;
            else
                dx = 0;
        }

        // make sure platform doesnt exceed left border
        if(platform.getX() + dx <= 0) {
            if(nextDX > 0)
                dx = nextDX;
            else
                dx = 0;
        }

        platform.setFrame(platform.getX() + dx, platform.getY(), platform.getWidth(), platform.getHeight());
        platformIntervalX = new Interval((int)platform.getX(), (int)(platform.getX() + platform.getWidth()));
        platformIntervalY = new Interval((int)(platform.getY() - platform.getHeight()), (int)platform.getY());
        repaint();
    }
公共平台(int dx){
int nextDX=dx;
如果(暂停| |初始化){
dx=0;
}
//确保平台未超出右侧边界
if(platform.getX()+platform.getWidth()+dx>size.getWidth()){
如果(nextDX<0)
dx=nextDX;
其他的
dx=0;
}
//确保平台不超过左边框
if(platform.getX()+dx 0)
dx=nextDX;
其他的
dx=0;
}
platform.setFrame(platform.getX()+dx,platform.getY(),platform.getWidth(),platform.getHeight());
platformIntervalX=新间隔((int)platform.getX(),(int)(platform.getX()+platform.getWidth());
platformIntervalY=新间隔((int)(platform.getY()-platform.getHeight(),(int)platform.getY());
重新油漆();
}

解决方案是不要使用KeyListener的按键来移动精灵。关键是不要依赖硬件特定的按键频率,要使用摆动计时器来创建自己的频率。而是使用键绑定和Swing计时器。按键时开始计时,按键释放时停止计时

例如,运行此代码并按下并释放向上箭头键:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.*;

@SuppressWarnings("serial")
public class KeyBindingEg extends JPanel {
   private static final String UP_KEY_PRESSED = "up key pressed";
   private static final String UP_KEY_RELEASED = "up key released";
   private static final int UP_TIMER_DELAY = 50;
   private static final Color FLASH_COLOR = Color.red;

   private Timer upTimer;
   private JLabel label = new JLabel();

   public KeyBindingEg() {
      label.setFont(label.getFont().deriveFont(Font.BOLD, 32));
      label.setOpaque(true);
      add(label);

      setPreferredSize(new Dimension(400, 300));

      int condition = WHEN_IN_FOCUSED_WINDOW;
      InputMap inputMap = getInputMap(condition);
      ActionMap actionMap = getActionMap();
      KeyStroke upKeyPressed = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false);
      KeyStroke upKeyReleased = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true);

      inputMap.put(upKeyPressed, UP_KEY_PRESSED);
      inputMap.put(upKeyReleased, UP_KEY_RELEASED);

      actionMap.put(UP_KEY_PRESSED, new UpAction(false));
      actionMap.put(UP_KEY_RELEASED, new UpAction(true));

   }

   private class UpAction extends AbstractAction {
      private boolean onKeyRelease;

      public UpAction(boolean onKeyRelease) {
         this.onKeyRelease = onKeyRelease;
      }

      @Override
      public void actionPerformed(ActionEvent evt) {
         if (!onKeyRelease) {
            if (upTimer != null && upTimer.isRunning()) {
               return;
            }
            System.out.println("key pressed");
            label.setText(UP_KEY_PRESSED);

            upTimer = new Timer(UP_TIMER_DELAY, new ActionListener() {

               @Override
               public void actionPerformed(ActionEvent e) {
                  Color c = label.getBackground();
                  if (FLASH_COLOR.equals(c)) {
                     label.setBackground(null);
                     label.setForeground(Color.black);
                  } else {
                     label.setBackground(FLASH_COLOR);
                     label.setForeground(Color.white);
                  }
               }
            });
            upTimer.start();
         } else {
            System.out.println("Key released");
            if (upTimer != null && upTimer.isRunning()) {
               upTimer.stop();
               upTimer = null;
            }
            label.setText("");
         }
      }

   }

   private static void createAndShowGui() {
      KeyBindingEg mainPanel = new KeyBindingEg();

      JFrame frame = new JFrame("KeyBindingEg");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

编辑

或者一个更好的例子,一个基于一个箭头键的按键在任何方向上移动精灵的例子。没有遇到任何延迟:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.EnumMap;
import java.util.Map;

import javax.swing.*;

@SuppressWarnings("serial")
public class KeyBindingEg2 extends JPanel {
   enum Dir {
      LEFT("Left", KeyEvent.VK_LEFT, -1, 0),
      RIGHT("Right", KeyEvent.VK_RIGHT, 1, 0),
      UP("Up", KeyEvent.VK_UP, 0, -1),
      DOWN("Down", KeyEvent.VK_DOWN, 0, 1);

      private String name;
      private int keyCode;
      private int deltaX;
      private int deltaY;
      private Dir(String name, int keyCode, int deltaX, int deltaY) {
         this.name = name;
         this.keyCode = keyCode;
         this.deltaX = deltaX;
         this.deltaY = deltaY;
      }
      public String getName() {
         return name;
      }
      public int getKeyCode() {
         return keyCode;
      }
      public int getDeltaX() {
         return deltaX;
      }
      public int getDeltaY() {
         return deltaY;
      }      
   }
   public static final int TIMER_DELAY = 10;
   public static final int DELTA_X = 2;
   public static final int DELTA_Y = DELTA_X;
   public static final int SPRITE_WIDTH = 10;
   public static final int SPRITE_HEIGHT = SPRITE_WIDTH;
   private static final String PRESSED = "pressed";
   private static final String RELEASED = "released";
   private static final int PREF_W = 800;
   private static final int PREF_H = 650;
   private Map<Dir, Boolean> dirMap = new EnumMap<>(Dir.class);
   private int spriteX = 0;
   private int spriteY = 0;
   private BufferedImage sprite;
   private Timer animationTimer = new Timer(TIMER_DELAY, new AnimationListener());

   public KeyBindingEg2() {
      for (Dir dir : Dir.values()) {
         dirMap.put(dir, Boolean.FALSE);
      }
      sprite = createSprite();
      setKeyBindings();
      animationTimer.start();
   }

   private BufferedImage createSprite() {
      BufferedImage sprt = new BufferedImage(SPRITE_WIDTH, SPRITE_HEIGHT, BufferedImage.TYPE_INT_ARGB);
      Graphics g = sprt.getGraphics();
      g.setColor(Color.RED);
      g.fillRect(0, 0, SPRITE_WIDTH, SPRITE_HEIGHT);
      g.dispose();
      return sprt;
   }

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

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (sprite != null) {
         g.drawImage(sprite, spriteX, spriteY, this);
      }
   }

   private void setKeyBindings() {
      int condition = WHEN_IN_FOCUSED_WINDOW;
      InputMap inputMap = getInputMap(condition);
      ActionMap actionMap = getActionMap();

      for (Dir dir : Dir.values()) {
         KeyStroke keyPressed = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, false);
         KeyStroke keyReleased = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, true);

         inputMap.put(keyPressed, dir.toString() + PRESSED);
         inputMap.put(keyReleased, dir.toString() + RELEASED);

         actionMap.put(dir.toString() + PRESSED, new DirAction(dir, PRESSED));
         actionMap.put(dir.toString() + RELEASED, new DirAction(dir, RELEASED));
      }

   }

   private class AnimationListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         int newX = spriteX;
         int newY = spriteY;
         for (Dir dir : Dir.values()) {
            if (dirMap.get(dir)) {
               newX += dir.getDeltaX() * DELTA_X;
               newY += dir.getDeltaY() * DELTA_Y;
            }
         }
         if (newX < 0 || newY < 0) {
            return;
         }
         if (newX + SPRITE_WIDTH > getWidth() || newY + SPRITE_HEIGHT > getHeight()) {
            return;
         }
         spriteX = newX;
         spriteY = newY;
         repaint();         
      }
   }

   private class DirAction extends AbstractAction {

      private String pressedOrReleased;
      private Dir dir;

      public DirAction(Dir dir, String pressedOrReleased) {
         this.dir = dir;
         this.pressedOrReleased = pressedOrReleased;
      }

      @Override
      public void actionPerformed(ActionEvent evt) {
         if (pressedOrReleased.equals(PRESSED)) {
            dirMap.put(dir, Boolean.TRUE);
         } else if (pressedOrReleased.equals(RELEASED)) {
            dirMap.put(dir, Boolean.FALSE);
         }
      }

   }

   private static void createAndShowGui() {
      KeyBindingEg2 mainPanel = new KeyBindingEg2();

      JFrame frame = new JFrame("KeyBindingEg");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}
导入java.awt.Color;
导入java.awt.Dimension;
导入java.awt.Graphics;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.awt.event.KeyEvent;
导入java.awt.image.buffereImage;
导入java.util.EnumMap;
导入java.util.Map;
导入javax.swing.*;
@抑制警告(“串行”)
公共类KeyBindingEg2扩展了JPanel{
枚举目录{
左(“左”,KeyEvent.VK_左,-1,0),
右(“右”,KeyEvent.VK_右,1,0),
向上(“向上”,KeyEvent.VK_向上,0,-1),
向下(“向下”,KeyEvent.VK_向下,0,1);
私有字符串名称;
私钥密码;
私人所得税;
德尔泰私人酒店;
私有目录(字符串名、整数键码、整数deltaX、整数deltaY){
this.name=名称;
this.keyCode=keyCode;
this.deltaX=deltaX;
this.deltaY=deltaY;
}
公共字符串getName(){
返回名称;
}
public int getKeyCode(){
返回键码;
}
公共整数getDeltaX(){
退税;
}
公共int getDeltaY(){
返回三角洲;
}      
}
公共静态最终整数定时器延迟=10;
公共静态最终int DELTA_X=2;
公共静态最终整数DELTA_Y=DELTA_X;
公共静态最终整数精灵宽度=10;
公共静态最终整数精灵高度=精灵宽度;
private static final String PRESSED=“PRESSED”;
私有静态最终字符串RELEASED=“RELEASED”;
专用静态最终整型预调W=800;
专用静态最终int PREF_H=650;
私有映射dirMap=newenummap(Dir.class);
私有int spriteX=0;
私有int-spriteY=0;
私有缓冲图像精灵;
私有定时器animationTimer=新定时器(Timer_DELAY,new AnimationListener());
公钥绑定EG2(){
for(Dir:Dir.values()){
dirMap.put(dir,Boolean.FALSE);
}
sprite=createSprite();
setKeyBindings();
animationTimer.start();
}
私有缓冲区映像createSprite(){
BuffereImage sprt=新的BuffereImage(精灵宽度、精灵高度、BuffereImage.TYPE_INT_ARGB);
Graphics g=sprt.getGraphics();
g、 setColor(Color.RED);
g、 fillRect(0,0,精灵宽度,精灵高度);
g、 处置();
返回sprt;
}
@凌驾
公共维度getPreferredSize(){
返回新维度(PREF_W,PREF_H);
}
@凌驾
受保护组件(图形g){
超级组件(g);
如果(精灵!=null){
g、 drawImage(sprite、spriteX、spriteY、this);
}
}
私有void setKeyBindings(){
int condition=当在聚焦窗口中时;
InputMap InputMap=getInputMap(条件);
ActionMap ActionMap=getActionMap();
for(Dir:Dir.values()){
KeyStroke keyPressed=KeyStroke.getKeyStroke(dir.getKeyCode(),0,false);
击键释放=击键.getKeyStroke(dir.getKeyCode(),0,true);
inputMap.put(按下键,dir.toString()+按下);
inputMap.put(keyreased,dir.toString()+RELEASED);
put(dir.toString()+按下,新的DirAction(dir,按下));
put(dir.toString()+RELEASED,new DirAction(dir,RELEASED));
}
}
私有类AnimationListener实现ActionListener{
@凌驾
P