简体   繁体   English

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

[英]How do I keep an object moving using arrow keys?

I am making a snake game, and I want my snake to be moving continuously once a key is pressed. 我正在做蛇游戏,我希望一旦按下按键,蛇就可以连续运动。 So, I press the down key, and it keeps moving even if the key is released. 因此,我按下了下键,即使释放键,它也会继续移动。 Right now, it just moves while the key is being held down. 现在,它只是在按住键的同时移动。

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

The "mySegment [0]" is the snake, and the "moveSouth" or whatever direction just moves it 5 pixels in that directin “ mySegment [0]”是蛇,“ moveSouth”或任何方向只是将其在该方向上移动了5个像素

Use a "game loop" to drive the animation. 使用“游戏循环”来驱动动画。 Since this looks to be possibly a Swing or AWT GUI, then your best bet is to use a Swing Timer -- please check out the tutorial . 由于这看起来可能是Swing或AWT GUI,所以最好的选择是使用Swing计时器-请查看教程 The gist is that within the Timer's ActionListener you increment the position of the snake, changing its direction depending on the state of your key press. 要点是,您可以在Timer的ActionListener中增加蛇的位置,并根据按键状态更改其方向。

I would use an enum to indicate Direction {UP, DOWN, LEFT, RIGHT}: 我将使用一个枚举来指示方向{上,下,左,右}:

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

AND then a Map<Direction, Boolean> to indicate which direction to head: 然后使用Map<Direction, Boolean>来指示要朝哪个方向前进:

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

Elsewhere you would initialize the Map to hold false in all values: 在其他地方,您将初始化Map,使其所有值均保持为false:

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

Then change the state of the items in the Map within your code for listening to key presses and releases -- call map.put(Direction.UP, true) for instance when the up key is pressed, and likewise call map.put(Direction.UP, false) when it has been released, same for the other keys. 然后在代码中更改Map中各项的状态,以侦听按键的按下和释放-例如,当按下向上键时调用map.put(Direction.UP, true) ,同样调用map.put(Direction.UP, false)释放时,其他键也是如此。 Note that if yours is a Swing application, I'd use Key Bindings and not a KeyListener to do this. 请注意,如果您的应用程序是Swing应用程序,那么我将使用键绑定而不是KeyListener来执行此操作。 In the listener, I'd then call repaint() on the GUI. 然后,在侦听器中,我将在GUI上调用repaint()

Within the Swing Timer, iterate through the Map, setting the direction based on the state of the Map. 在Swing计时器内,迭代Map,并根据Map的状态设置方向。

class fields: 类字段:

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

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

Within the ActionListener 在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();

For example (not a snake but a ball -- I'll leave the Snake to you) 例如(不是蛇,而是球-我将蛇交给你)

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

}

If you want to keep the snake moving you need some kind of game-loop (as mentioned before). 如果您想让蛇保持运动,则需要某种游戏循环(如前所述)。 The easiest way is having a single field containing the current direction, and set it based on the input. 最简单的方法是使用一个包含当前方向的字段,然后根据输入进行设置。 So here are a few methods/classes you could use to set the correct direction/position 因此,这里有一些方法/类可用于设置正确的方向/位置

The Direction Enum 方向枚举

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

The method to set the current direction. 设置当前方向的方法。 (This assumes there is a field named 'currentDirection' which contains an enum (Direction) representing the current direction) (假定存在一个名为“ currentDirection”的字段,其中包含表示当前方向的枚举(方向))

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

This is somewhere in your game loop (either an timer, or a while loop including a 'sleep' call to prevent CPU hugging) 这是您游戏循环中的某个位置(计时器或while循环,其中包括“ sleep”调用以防止CPU阻塞)

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

And of course instead of calling 'mySegment[0].moveNorth();' 当然不用调用“ mySegment [0] .moveNorth();” or someting equalivant in the actionHandlers for the keyEvents, you should only call 'setDirection();' 或keyEvent的actionHandler中的等效项,您仅应调用“ setDirection();” to make the snake move. 使蛇移动。

I hope this helped you out. 希望这对您有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM