Gradually accelerate sprite on key pressed; gradually decelerate on key released

后端 未结 4 1159
甜味超标
甜味超标 2020-12-21 11:16

I\'ve been trying to figure out how I can gradually accelerate a sprite on key pressed and then once the key is released, gradually slow down to a stop, muc

4条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-21 11:33

    Essentially acceleration and deceleration is the change of speed over time.

    This assumes a few things.

    1. You are updating the state at a regular interval, so the acceleration/deceleration can be applied to the objects current speed
    2. You can monitor the state of a given input, so you know when and what should be applied

    This example uses a Swing Timer as the main "game loop". This is used to continuously update the state of the player object.

    It uses the key bindings API to change a GameState object which carries information about the current inputs into the game, this is used by the Player to make decisions about what deltas should be applied.

    The example has quite a large maximum speed/rotation, so you can play with these, but if you release all the inputs, the game object will decelerate back to a "neutral" position (of 0 delta inputs).

    If you apply input in one direction for short period of time, then apply an input into the opposite direction, the player will need to decelerate down through 0 and then accelerate in the opposite direction (to a maximum speed), so it has breaking

    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyEvent;
    import java.awt.image.BufferedImage;
    import java.awt.image.ImageObserver;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    import javax.imageio.ImageIO;
    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 javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Ponies {
    
        public static void main(String[] args) {
            new Ponies();
        }
    
        public Ponies() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new GameView());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class GameView extends JPanel {
    
            private GameState gameState;
            private Player player;
    
            public GameView() {
                gameState = new GameState();
                addKeyBindingForInput(GameInput.DOWN, KeyEvent.VK_S);
                addKeyBindingForInput(GameInput.UP, KeyEvent.VK_W);
                addKeyBindingForInput(GameInput.LEFT, KeyEvent.VK_A);
                addKeyBindingForInput(GameInput.RIGHT, KeyEvent.VK_D);
                addKeyBindingForInput(GameInput.ROTATE_LEFT, KeyEvent.VK_LEFT);
                addKeyBindingForInput(GameInput.ROTATE_RIGHT, KeyEvent.VK_RIGHT);
    
                try {
                    player = new Player(400, 400);
    
                    Timer timer = new Timer(40, new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            player.update(GameView.this, gameState);
                            repaint();
                        }
                    });
                    timer.start();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
    
            protected void addKeyBindingForInput(GameInput input, int virtualKey) {
                InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
                ActionMap actionMap = getActionMap();
    
                inputMap.put(KeyStroke.getKeyStroke(virtualKey, 0, false), input + ".pressed");
                actionMap.put(input + ".pressed", new GameInputAction(gameState, input, true));
    
                inputMap.put(KeyStroke.getKeyStroke(virtualKey, 0, true), input + ".released");
                actionMap.put(input + ".released", new GameInputAction(gameState, input, false));
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(400, 400);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                player.paint(g2d, this);
    
                List inputs = gameState.getInputs();
                FontMetrics fm = g2d.getFontMetrics();
                int y = getHeight() - (fm.getHeight() * inputs.size());
                for (GameInput input : inputs) {
                    String text = input.name();
                    g2d.drawString(text, getWidth() - fm.stringWidth(text), y + fm.getAscent());
                    y += fm.getHeight();
                }
                g2d.dispose();
            }
    
        }
    
        public class GameInputAction extends AbstractAction {
    
            private final GameState gameState;
            private final GameInput input;
            private final boolean pressed;
    
            public GameInputAction(GameState gameState, GameInput input, boolean pressed) {
                this.gameState = gameState;
                this.input = input;
                this.pressed = pressed;
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                if (pressed) {
                    gameState.addInput(input);
                } else {
                    gameState.removeInput(input);
                }
            }
    
        }
    
        public enum GameInput {
            LEFT, RIGHT,
            UP, DOWN,
            ROTATE_LEFT, ROTATE_RIGHT
        }
    
        public class GameState {
    
            private Set inputs;
    
            public GameState() {
                inputs = new HashSet<>(25);
            }
    
            public boolean hasInput(GameInput input) {
                return inputs.contains(input);
            }
    
            public void addInput(GameInput input) {
                inputs.add(input);
            }
    
            public void removeInput(GameInput input) {
                inputs.remove(input);
            }
    
            public List getInputs() {
                return new ArrayList(inputs);
            }
    
        }
    
        public static class Player {
    
            protected static final int MAX_DELTA = 20;
            protected static final int MAX_ROTATION_DELTA = 20;
    
            protected static final int MOVE_DELTA = 4;
            protected static final int ROTATION_DELTA = 4;
    
            private int x, y;
            private int xDelta, yDelta;
            private BufferedImage sprite;
    
            private double angle;
            private double rotationDelta;
    
            public Player(int width, int height) throws IOException {
                sprite = ImageIO.read(getClass().getResource("/PlayerSprite.png"));
                x = (width - sprite.getWidth()) / 2;
                y = (height - sprite.getHeight()) / 2;
            }
    
            public void update(Container container, GameState state) {
                if (state.hasInput(GameInput.LEFT)) {
                    xDelta -= MOVE_DELTA;
                } else if (state.hasInput(GameInput.RIGHT)) {
                    xDelta += MOVE_DELTA;
                } else if (xDelta < 0) {
                    xDelta++;
                } else if (xDelta > 0) {
                    xDelta--;
                }
                if (state.hasInput(GameInput.UP)) {
                    yDelta -= MOVE_DELTA;
                } else if (state.hasInput(GameInput.DOWN)) {
                    yDelta += MOVE_DELTA;
                } else if (yDelta < 0) {
                    yDelta++;
                } else if (yDelta > 0) {
                    yDelta--;
                }
                if (state.hasInput(GameInput.ROTATE_LEFT)) {
                    rotationDelta -= MOVE_DELTA;
                } else if (state.hasInput(GameInput.ROTATE_RIGHT)) {
                    rotationDelta += MOVE_DELTA;
                } else if (rotationDelta < 0) {
                    rotationDelta++;
                } else if (rotationDelta > 0) {
                    rotationDelta--;
                }
    
                xDelta = Math.max(-MAX_DELTA, Math.min(xDelta, MAX_DELTA));
                yDelta = Math.max(-MAX_DELTA, Math.min(yDelta, MAX_DELTA));
                rotationDelta = Math.max(-MAX_ROTATION_DELTA, Math.min(rotationDelta, MAX_ROTATION_DELTA));
    
                x += xDelta;
                y += yDelta;
                angle += rotationDelta;
    
                if (x < -sprite.getWidth()) {
                    x = container.getWidth();
                } else if (x + sprite.getWidth() > container.getWidth() + sprite.getWidth()) {
                    x = 0;
                }
    
                if (y < -sprite.getHeight()) {
                    y = container.getHeight();
                } else if (y + sprite.getHeight() > container.getHeight() + sprite.getHeight()) {
                    y = 0;
                }
            }
    
            public void paint(Graphics2D g2d, ImageObserver io) {
                Graphics2D copy = (Graphics2D) g2d.create();
                copy.translate(x, y);
                copy.rotate(Math.toRadians(angle), sprite.getWidth() / 2.0d, sprite.getHeight() / 2.0d);
                copy.drawImage(sprite, 0, 0, io);
                copy.dispose();
            }
    
        }
    
    }
    

    Take a look at How to Use Key Bindings and How to use Swing Timers for more details

提交回复
热议问题