【问题标题】:How do I keep an object moving using arrow keys?如何使用箭头键保持对象移动?
【发布时间】:2016-12-01 21:16:45
【问题描述】:

我正在制作一个蛇游戏,我希望我的蛇在按下一个键后连续移动。所以,我按下向下键,即使松开键,它也会继续移动。现在,它只是在按住键时移动。

    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
           mySegment[0].moveSouth();
           repaint();
        }
    else if (e.getKeyCode() == KeyEvent.VK_UP) {
        mySegment[0].moveNorth();
        repaint();
    }
    else if(e.getKeyCode() == KeyEvent.VK_LEFT){
        mySegment[0].moveWest();
        repaint();
    }
    else if (e.getKeyCode() == KeyEvent.VK_RIGHT){
        mySegment[0].moveEast();
        repaint();
    }

    for (int a = 0; a < 10; a++) {
        if (myFruit[a].distance (mySegment[0].getX(), mySegment[0].getY())                
        <= 20) {
            myFruit[a].hide();
        }
    }

“mySegment [0]”是蛇,而“moveSouth”或任何方向只是将它沿该方向移动 5 个像素

【问题讨论】:

  • 让它调用一个循环移动直到被中断的方法。

标签: java key-events


【解决方案1】:

使用“游戏循环”来驱动动画。由于这看起来可能是一个 Swing 或 AWT GUI,那么最好的办法是使用 Swing 计时器——请查看tutorial。要点是在 Timer 的 ActionListener 中增加蛇的位置,根据按键的状态改变它的方向。

我会使用枚举来指示方向 {UP, DOWN, LEFT, RIGHT}:

public enum Direction {
    UP,
    DOWN,
    LEFT,
    RIGHT
}

然后是 Map&lt;Direction, Boolean&gt; 以指示前进的方向:

private Map<Direction, Boolean> dirMap = new EnumMap<>(Direction.class);

在其他地方,您将初始化 Map 以在所有值中保持 false:

// initialize the map to all false
for (Direction dir : Direction.values()) {
    dirMap.put(dir, false);
}

然后在您的代码中更改 Map 中项目的状态以监听按键的按下和释放——例如在按下向上键时调用 map.put(Direction.UP, true),并在释放时同样调用 map.put(Direction.UP, false),其他键相同。请注意,如果您的应用程序是 Swing 应用程序,我会使用 Key Bindings 而不是 KeyListener 来执行此操作。在监听器中,我会在 GUI 上调用 repaint()

在 Swing Timer 中,遍历地图,根据地图的状态设置方向。

类字段:

private int spriteX = 0; // location of sprite
private int spriteY = 0;

private int directionX = 0; // direction sprite is heading
private int directionY = 0;

在 ActionListener 中

// within the Swing Timer's ActionListener
if (dirMap.get(Direction.UP)) {
    directionY -= 1;
}
if (dirMap.get(Direction.DOWN)) {
    directionY += 1;
}
if (dirMap.get(Direction.RIGHT)) {
    directionX += 1;
}
if (dirMap.get(Direction.LEFT)) {
    directionY -= 1;
}

// here multiply directionX and directionY by some scale factor and use to place new snake head
// then call repaint();

例如(不是蛇,而是球——我会把蛇留给你)

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.EnumMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.*;

@SuppressWarnings("serial")
public class DirTest extends JPanel {
    private static final int PREF_W = 800;
    private static final int PREF_H = PREF_W;
    private static final int TIMER_DELAY = 40;
    private static final Color SPRITE_COLOR = Color.RED;
    private static final int SPRITE_W = 20;
    private static final Color BG = Color.BLACK;
    public static final int SCALE = 1;
    private Map<Direction, Boolean> dirMap = new EnumMap<>(Direction.class);
    private int spriteX = 0;
    private int spriteY = 0;
    private int directionX = 0;
    private int directionY = 0;
    private Timer gameLoopTimer = new Timer(TIMER_DELAY, new TimerListener());

    public DirTest() {
        setKeyBindings();

        setBackground(BG);
        // initialize map to all 0;
        for (Direction dir : Direction.values()) {
            dirMap.put(dir, false);
        }

        gameLoopTimer.start();
    }

    private void setKeyBindings() {
        int condition = WHEN_IN_FOCUSED_WINDOW; // bind to keys if component in active window
        InputMap inputMap = getInputMap(condition);
        ActionMap actionMap = getActionMap();

        setKeyBinding(inputMap, actionMap, KeyEvent.VK_UP, Direction.UP);
        setKeyBinding(inputMap, actionMap, KeyEvent.VK_DOWN, Direction.DOWN);
        setKeyBinding(inputMap, actionMap, KeyEvent.VK_LEFT, Direction.LEFT);
        setKeyBinding(inputMap, actionMap, KeyEvent.VK_RIGHT, Direction.RIGHT);
    }

    private void setKeyBinding(InputMap inputMap, ActionMap actionMap, int keyCode, Direction dir) {
        KeyStroke press = KeyStroke.getKeyStroke(keyCode, 0, false);
        KeyStroke released = KeyStroke.getKeyStroke(keyCode, 0, true);

        Action pressAction = new PressedAction(dir, true);
        Action releasedAction = new PressedAction(dir, false);

        inputMap.put(press, press.toString());
        inputMap.put(released, released.toString());

        actionMap.put(press.toString(), pressAction);
        actionMap.put(released.toString(), releasedAction);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(SPRITE_COLOR);
        g2.fillOval(spriteX, spriteY, SPRITE_W, SPRITE_W);
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PREF_W, PREF_H);
    }

    private class PressedAction extends AbstractAction {
        private boolean pressed;
        private Direction dir;

        public PressedAction(Direction dir, boolean pressed) {
            this.dir = dir;
            this.pressed = pressed;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            dirMap.put(dir, pressed);
        }
    }

    private class TimerListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            for (Entry<Direction, Boolean> entry : dirMap.entrySet()) {
                if (entry.getValue()) {
                    directionX += entry.getKey().getX();
                    directionY += entry.getKey().getY();
                }
            }

            spriteX += SCALE * directionX;
            spriteY += SCALE * directionY;

            repaint();
        }
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("DirTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new DirTest());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

enum Direction {
    UP(0, -1),
    DOWN(0, 1),
    LEFT(-1, 0),
    RIGHT(1, 0);

    private int x;
    private int y;

    private Direction(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public int getX() {
        return x;
    }
    public int getY() {
        return y;
    }

}

【讨论】:

  • 这样 OP 需要确保 HashMap 中只有一个值设置为true,否则它可能会表现得很奇怪。我会选择您设置/检查当前方向的单个字段。此外,在使用带有枚举的 Map 实现时,EnumHasMap 可能是更好的选择。
  • 此外,OP 正在寻找一条蛇继续移动的方式,即使钥匙已被释放。所以也许稍微修改一下设置。截至目前,这将产生与 OP 当前相同的结果
  • @n247s:请查看并运行代码。即使松开按键,这也会导致继续运动。更重要的是,如果按住键,动作会变得更快。试试看。
  • @n247s: 并且 Map is 是一个 EnumMap,但您仍需将变量 声明 到接口的良好的 OOP 实践。
  • "This way the OP needs to make sure only one value in the HashMap is set to true" -- 不是这样。如果用户同时按下左键和右键,我的设置会将两者都设置为 true,从而相互抵消,这是预期和期望的行为。
【解决方案2】:

如果您想让蛇继续移动,您需要某种游戏循环(如前所述)。最简单的方法是拥有一个包含当前方向的字段,并根据输入进行设置。所以这里有一些方法/类可以用来设置正确的方向/位置

方向枚举

public enum  Direction
{
    NORTH, EAST, SOUTH, WEST;

    public Direction oposite()
    {
         switch(this)
         {
             case NORTH: return SOUTH;
             case SOUTH: return NORTH;
             case EAST: return WEST;
             case WEST: return EAST;
          }
    }
}

设置当前方向的方法。 (假设有一个名为 'currentDirection' 的字段包含一个表示当前方向的枚举(Direction))

  public void setDirection(Direction newDirection)
 {
      if(currentDirection != newDirection.oposite())
          currentDirection = newDirection;
 }

这是你的游戏循环中的某个地方(一个计时器,或者一个包含“睡眠”调用以防止 CPU 占用的 while 循环)

     switch(currentDirection)
     {
          case NORTH: mySegment[0].moveNorth(); break;
          case EAST: mySegment[0].moveEast(); break;
          case SOUTH: mySegment[0].moveSouth(); break;
          case WEST: mySegment[0].moveWest(); break;
     }
     repaint();

当然不是调用 'mySegment[0].moveNorth();'或 keyEvents 的 actionHandlers 中的某些等价物,您应该只调用 'setDirection();'让蛇动起来。

希望对你有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多