我怎样才能同时 运行 代码块与键绑定?
How can I simultaneity run blocks of code with key bindings?
我目前正在学习如何使用键绑定。我在玩我写的代码,我注意到当我按下两个键(箭头键)时,只有最后一个键会 运行。我应该简单地使用 KeyListener 还是有办法让它工作?既然是游戏,就必须能够同时 运行 超过 4 个按键。
package game;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
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 game.sfx.Screen;
import game.sfx.SpriteSheet;
public class Game implements Runnable{
private static final long serialVersionUID = 1L;
public static final int WIDTH = 160;
public static final int HEIGHT = WIDTH / 12 * 9;
public static final int SCALE = 3;
public static final String NAME = "Game";
private JFrame frame;
private JPanel panel;
public boolean running = false;
public int tickCount = 0;
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
Screen screen;
public Game(){
panel = new JPanel();
panel.setMinimumSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
panel.setMaximumSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
panel.setPreferredSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
frame = new JFrame(NAME);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(panel, BorderLayout.CENTER);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
InputMap im = panel.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW); // key binding here
ActionMap am = panel.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "r");
am.put("r", new InputHandler("right", this));
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "l");
am.put("l", new InputHandler("left", this));
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "u");
am.put("u", new InputHandler("up", this));
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "d");
am.put("d", new InputHandler("down", this));
}
public void init(){
screen = new Screen(WIDTH, HEIGHT, new SpriteSheet("res/Untitled.png"));
}
public synchronized void start() {
running = true;
new Thread(this).start();
}
public synchronized void stop() {
running = false;
}
public void run() {
long lastTime = System.nanoTime();
double nsPerTick = 1000000000D/60D;
int frames = 0;
int ticks = 0;
long lastTimer = System.currentTimeMillis();
double delta = 0;
init();
while (running){
long now = System.nanoTime();
delta += (now - lastTime) / nsPerTick;
lastTime = now;
boolean shouldRender = true;
while(delta >= 1)
{
ticks++;
tick();
delta -= 1;
shouldRender = true;
}
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (shouldRender)
{
frames++;
render();
}
if(System.currentTimeMillis() - lastTimer >= 1000){
lastTimer += 1000;
System.out.println(ticks + " ticks, " + frames + " frames");
frames = 0;
ticks = 0;
}
}
}
public void tick(){
tickCount++;
}
public void render(){
screen.render(pixels, 0, WIDTH);
Graphics g = panel.getGraphics();
g.drawImage(image, 0, 0, panel.getWidth(), panel.getHeight(), null);
g.dispose();
}
public static void main(String[] args){
new Game().start();
}
}
class InputHandler extends AbstractAction {
/**
*
*/
private static final long serialVersionUID = 1L;
private String key;
private Game game;
public InputHandler(String key, Game game) {
this.key = key;
this.game = game;
}
@Override
public void actionPerformed(ActionEvent e) {
switch(key){
case "right":
game.screen.xOffset+=2;
break;
case "left":
game.screen.xOffset-=2;
break;
case "up":
game.screen.yOffset+=2;
break;
case "down":
game.screen.yOffset-=2;
break;
}
}
}
这是代码
按照我在 linked to answer 中的建议进行操作:
- 使用 Swing Timer 创建游戏循环
- 创建一个名为 Direction 的枚举,其中包含 UP、DOWN、LEFT 和 RIGHT 项
- 而且还有指示实际方向的 int 矢量场
- 创建一个
HashMap<Direction, Boolean>
,当按下箭头键时会发生变化
- 让游戏循环控制这张地图,并根据映射到 Boolean.TRUE 的方向改变方向。
- 设置您的键绑定以简单地更改 HashMap 的状态,仅此而已。
and I noticed when I press two keys (arrow keys), only the last one would run
因为只为最后一次按下的键生成事件
Should I simply use KeyListener
无论您使用 KeyListener 还是 KeyBindings,都是如此。它是生成事件的 OS。
Since its a game, it would have to be able to run over 4 keys at the same time.
所以解决方案是按原样跟踪每个键 pressed/released
查看 Motion Using the Keyboard。 KeyboardAnimation
示例显示了如何使用 KeyBindings 完成此操作。
我目前正在学习如何使用键绑定。我在玩我写的代码,我注意到当我按下两个键(箭头键)时,只有最后一个键会 运行。我应该简单地使用 KeyListener 还是有办法让它工作?既然是游戏,就必须能够同时 运行 超过 4 个按键。
package game;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
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 game.sfx.Screen;
import game.sfx.SpriteSheet;
public class Game implements Runnable{
private static final long serialVersionUID = 1L;
public static final int WIDTH = 160;
public static final int HEIGHT = WIDTH / 12 * 9;
public static final int SCALE = 3;
public static final String NAME = "Game";
private JFrame frame;
private JPanel panel;
public boolean running = false;
public int tickCount = 0;
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
Screen screen;
public Game(){
panel = new JPanel();
panel.setMinimumSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
panel.setMaximumSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
panel.setPreferredSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE));
frame = new JFrame(NAME);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(panel, BorderLayout.CENTER);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
InputMap im = panel.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW); // key binding here
ActionMap am = panel.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "r");
am.put("r", new InputHandler("right", this));
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "l");
am.put("l", new InputHandler("left", this));
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "u");
am.put("u", new InputHandler("up", this));
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "d");
am.put("d", new InputHandler("down", this));
}
public void init(){
screen = new Screen(WIDTH, HEIGHT, new SpriteSheet("res/Untitled.png"));
}
public synchronized void start() {
running = true;
new Thread(this).start();
}
public synchronized void stop() {
running = false;
}
public void run() {
long lastTime = System.nanoTime();
double nsPerTick = 1000000000D/60D;
int frames = 0;
int ticks = 0;
long lastTimer = System.currentTimeMillis();
double delta = 0;
init();
while (running){
long now = System.nanoTime();
delta += (now - lastTime) / nsPerTick;
lastTime = now;
boolean shouldRender = true;
while(delta >= 1)
{
ticks++;
tick();
delta -= 1;
shouldRender = true;
}
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (shouldRender)
{
frames++;
render();
}
if(System.currentTimeMillis() - lastTimer >= 1000){
lastTimer += 1000;
System.out.println(ticks + " ticks, " + frames + " frames");
frames = 0;
ticks = 0;
}
}
}
public void tick(){
tickCount++;
}
public void render(){
screen.render(pixels, 0, WIDTH);
Graphics g = panel.getGraphics();
g.drawImage(image, 0, 0, panel.getWidth(), panel.getHeight(), null);
g.dispose();
}
public static void main(String[] args){
new Game().start();
}
}
class InputHandler extends AbstractAction {
/**
*
*/
private static final long serialVersionUID = 1L;
private String key;
private Game game;
public InputHandler(String key, Game game) {
this.key = key;
this.game = game;
}
@Override
public void actionPerformed(ActionEvent e) {
switch(key){
case "right":
game.screen.xOffset+=2;
break;
case "left":
game.screen.xOffset-=2;
break;
case "up":
game.screen.yOffset+=2;
break;
case "down":
game.screen.yOffset-=2;
break;
}
}
}
这是代码
按照我在 linked to answer 中的建议进行操作:
- 使用 Swing Timer 创建游戏循环
- 创建一个名为 Direction 的枚举,其中包含 UP、DOWN、LEFT 和 RIGHT 项
- 而且还有指示实际方向的 int 矢量场
- 创建一个
HashMap<Direction, Boolean>
,当按下箭头键时会发生变化 - 让游戏循环控制这张地图,并根据映射到 Boolean.TRUE 的方向改变方向。
- 设置您的键绑定以简单地更改 HashMap 的状态,仅此而已。
and I noticed when I press two keys (arrow keys), only the last one would run
因为只为最后一次按下的键生成事件
Should I simply use KeyListener
无论您使用 KeyListener 还是 KeyBindings,都是如此。它是生成事件的 OS。
Since its a game, it would have to be able to run over 4 keys at the same time.
所以解决方案是按原样跟踪每个键 pressed/released
查看 Motion Using the Keyboard。 KeyboardAnimation
示例显示了如何使用 KeyBindings 完成此操作。