如何使用箭头键保持对象移动?

时间:2016-12-01 21:16:46

标签: java key-events

我正在制作蛇游戏,我希望一旦按下按键,我的蛇就会连续移动。所以,我按下向下键,即使释放按键也会继续移动。现在,它只是在键被按下时移动。

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

&#34; mySegment [0]&#34;是蛇,而且是'南方&#34;或任何方向只是移动它直接5 <像素

2 个答案:

答案 0 :(得分:0)

使用“游戏循环”来驱动动画。由于这看起来可能是Swing或AWT GUI,因此最好的办法是使用Swing Timer - 请查看tutorial。要点是在Timer的ActionListener中,您可以增加蛇的位置,根据按键状态改变方向。

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

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

然后使用Map<Direction, Boolean>来指示前往哪个方向:

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.put(Direction.UP, true),同时在调用时调用map.put(Direction.UP, false)已被释放,其他键也一样。请注意,如果你的是Swing应用程序,我会使用Key Bindings而不是KeyListener来执行此操作。在监听器中,我然后在GUI上调用repaint()

在Swing Timer中,迭代Map,根据Map的状态设置方向。

类字段:

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

}

答案 1 :(得分:0)

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

方向枚举

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

设置当前方向的方法。 (这假设有一个名为&#39; currentDirection&#39;的字段,其中包含表示当前方向的枚举(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();&#39;或者在keyEvents的actionHandlers中使用equitant,你应该只调用&#39; setDirection();&#39;使蛇移动。

我希望这能帮到你。