Java密钥绑定的启动非常不一致



我正在开发一个游戏,其中有一个玩家我想使用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);
}
}

至于"为什么"你有问题,我只能猜测,因为我们只有一个断章取义的片段需要处理,然而,有很多事情可以改进,这可能有助于解决问题。

  • 调用getInputMap时使用WHEN_IN_FOCUSED_WINDOW

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

  • 更喜欢覆盖paintComponent而不是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);
}
});
}
}

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

最新更新