Continuous movement with a single key press?

前端 未结 2 1287
一生所求
一生所求 2021-01-16 07:07

I\'m trying to make a program in java that involves making an object move constantly from a single key press. Think Pacman, where you press up once and Pacman continues to g

相关标签:
2条回答
  • 2021-01-16 07:30

    The basic concept revolves around this idea of a "delta" or "change" value. This value is then applied to the state you want to change by either incrementing or decrementing the state value by it.

    Because of the nature of Swing, you can't block the Event Dispatching Thread, otherwise you end up preventing from processing incoming events (such as paint and key events).

    Equally, you should never try and update any UI component (or state variable that might effect the UI) from any thread other then the EDT.

    While there are tricks you can apply to facilitate these requirements, the simplest is to use a javax.swing.Timer, which triggers a actionPerformed event on a regular bases within the EDT.

    When this occurs you "update" all the elements by the prescribed amount and repaint the screen.

    enter image description hereenter image description here

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    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.geom.AffineTransform;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    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 PacManTest {
    
        public static void main(String[] args) {
            new PacManTest();
        }
    
        public PacManTest() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("Test");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new MazePane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class PacMan {
    
            private int x;
            private int y;
    
            private int deltaX;
            private int deltaY;
    
            private BufferedImage sprite;
    
            public PacMan() {
                try {
                    sprite = ImageIO.read(new File("PacMan.png"));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
    
            public void move(int x, int y) {
                deltaX = x;
                deltaY = y;
            }
    
            public void update(MazePane pane) {
                x += deltaX;
                y += deltaY;
                if (x + sprite.getWidth() > pane.getWidth()) {
                    x = pane.getWidth() - sprite.getWidth();
                } else if (x < 0) {
                    x = 0;
                }
                if (y + sprite.getHeight() > pane.getHeight()) {
                    y = pane.getHeight() - sprite.getHeight();
                } else if (y < 0) {
                    y = 0;
                }
            }
    
            public void paint(MazePane pane, Graphics2D g2d) {
                Graphics2D g = (Graphics2D) g2d.create();
    
                float angle = 0;
                if (deltaX != 0) {
                    angle = deltaX > 0 ? 0 : 180;
                } else if (deltaY != 0) {
                    angle = deltaY > 0 ? 90 : 270;                
                }
                AffineTransform t = new AffineTransform();
                t.translate(x, y);
                t.rotate(Math.toRadians(angle), sprite.getWidth() / 2, sprite.getHeight() / 2);
                g.setTransform(t);
                g.drawImage(sprite, 0, 0, pane);
                g.dispose();
            }
    
        }
    
        public class MazePane extends JPanel {
    
            private PacMan pacMan;
            public MazePane() {
                pacMan = new PacMan();
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        pacMan.update(MazePane.this);
                        repaint();
                    }
                });
                timer.start();
                InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
                ActionMap am = getActionMap();
    
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "down");
    
                am.put("left", new MoveAction(pacMan, -4, 0));
                am.put("right", new MoveAction(pacMan, 4, 0));
                am.put("up", new MoveAction(pacMan, 0, -4));
                am.put("down", new MoveAction(pacMan, 0, 4));
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                pacMan.paint(this, g2d);
                g2d.dispose();
            }
    
            public class MoveAction extends AbstractAction {
    
                private int deltaX;
                private int deltaY;
                private PacMan pacMan;
    
                public MoveAction(PacMan pacMan, int deltaX, int deltaY) {
                    this.deltaX = deltaX;
                    this.deltaY = deltaY;
                    this.pacMan = pacMan;
                }
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    pacMan.move(deltaX, deltaY);
                }
    
            }        
        }
    }
    

    I would also recommend that you take the time to learn about Key Bindings, KeyListener suffer from focus issues, which key bindings are capable of addressing...

    0 讨论(0)
  • 2021-01-16 07:52

    You need to process the move in a separate thread. I.e.:

    public class Pacman implements Runnable
    {
        public void run(){
            //moving code, i.e. in a while loop
    
            //every move will notify the EDT:
            SwingUtilities.invokeLater(new Runnable(){
                public void run(){
                    //update the Swing here - i.e. move Pacman
                }
            }
        }
    
        public void startMoving(){
            new Thread(this).start();
        }
    
        //more methods to set speed, direction, etc...
    }
    

    Then you keep a reference to an instance of Pacman class in your Gui class and respond to various key presses by changing pacman's parameters:

    public void keyPressed(KeyEvent e){
        int keyCode = e.getKeyCode();
        if(keyCode == e.VK_LEFT){
            pacman.newDirection(LEFT); //for exmaple, enum with direction LEFT, RIGHT, UP, DOWN...
        }
        //etc... more logic
    }
    
    0 讨论(0)
提交回复
热议问题