Java键盘输入 – 游戏开发

对于我正在为课堂创作的游戏,我有一个特定的“问题”。

游戏是“打破它”的实现。 为了在底部移动平台,我只使用了一个关键的监听器。 问题是在第一次按键之后,在平台开始移动之前存在短暂的“滞后”或“断续”。 我怎么能阻止它得到顺利的回应? 还有另一种方法而不是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_SPACE) { System.out.println("space"); gamePanel.play(); } if(e.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) 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(); } 

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

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

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