How to make an image move while listening to a keypress in Java.

后端 未结 3 540
一整个雨季
一整个雨季 2020-11-22 09:47

I\'m starting to learn java programming and I think it\'s cool to learn java through game development. I know how to draw image and listen to a keypress then move that image

相关标签:
3条回答
  • 2020-11-22 09:52

    As an alternative to KeyListener, consider using actions and key bindings, discussed here. Derived from this example, the program below moves a line left, down, up or right using either buttons or keys.

    import java.awt.BasicStroke;
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import javax.swing.AbstractAction;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.KeyStroke;
    
    /**
     * @see https://stackoverflow.com/questions/6991648
     * @see https://stackoverflow.com/questions/6887296
     * @see https://stackoverflow.com/questions/5797965
     */
    public class LinePanel extends JPanel {
    
        private MouseHandler mouseHandler = new MouseHandler();
        private Point p1 = new Point(100, 100);
        private Point p2 = new Point(540, 380);
        private boolean drawing;
    
        public LinePanel() {
            this.setPreferredSize(new Dimension(640, 480));
            this.addMouseListener(mouseHandler);
            this.addMouseMotionListener(mouseHandler);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(Color.blue);
            g2d.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setStroke(new BasicStroke(8,
                BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
            g.drawLine(p1.x, p1.y, p2.x, p2.y);
        }
    
        private class MouseHandler extends MouseAdapter {
    
            @Override
            public void mousePressed(MouseEvent e) {
                drawing = true;
                p1 = e.getPoint();
                p2 = p1;
                repaint();
            }
    
            @Override
            public void mouseReleased(MouseEvent e) {
                drawing = false;
                p2 = e.getPoint();
                repaint();
            }
    
            @Override
            public void mouseDragged(MouseEvent e) {
                if (drawing) {
                    p2 = e.getPoint();
                    repaint();
                }
            }
        }
    
        private class ControlPanel extends JPanel {
    
            private static final int DELTA = 10;
    
            public ControlPanel() {
                this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
                this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
                this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
                this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));
            }
    
            private class MoveButton extends JButton {
    
                KeyStroke k;
                int dx, dy;
    
                public MoveButton(String name, int code, final int dx, final int dy) {
                    super(name);
                    this.k = KeyStroke.getKeyStroke(code, 0);
                    this.dx = dx;
                    this.dy = dy;
                    this.setAction(new AbstractAction(this.getText()) {
    
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            LinePanel.this.p1.translate(dx, dy);
                            LinePanel.this.p2.translate(dx, dy);
                            LinePanel.this.repaint();
                        }
                    });
                    ControlPanel.this.getInputMap(
                        WHEN_IN_FOCUSED_WINDOW).put(k, k.toString());
                    ControlPanel.this.getActionMap().put(k.toString(), new AbstractAction() {
    
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            MoveButton.this.doClick();
                        }
                    });
                }
            }
        }
    
        private void display() {
            JFrame f = new JFrame("LinePanel");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(this);
            f.add(new ControlPanel(), BorderLayout.SOUTH);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new LinePanel().display();
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-11-22 09:57

    But basically I just want to know how to make the image move left to right while the window is listening to a keypress

    You can use a Swing Timer to animate an image:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class TimerAnimation extends JLabel implements ActionListener
    {
        int deltaX = 2;
        int deltaY = 3;
        int directionX = 1;
        int directionY = 1;
    
        public TimerAnimation(
            int startX, int startY,
            int deltaX, int deltaY,
            int directionX, int directionY,
            int delay)
        {
            this.deltaX = deltaX;
            this.deltaY = deltaY;
            this.directionX = directionX;
            this.directionY = directionY;
    
            setIcon( new ImageIcon("dukewavered.gif") );
    //      setIcon( new ImageIcon("copy16.gif") );
            setSize( getPreferredSize() );
            setLocation(startX, startY);
            new javax.swing.Timer(delay, this).start();
        }
    
        public void actionPerformed(ActionEvent e)
        {
            Container parent = getParent();
    
            //  Determine next X position
    
            int nextX = getLocation().x + (deltaX * directionX);
    
            if (nextX < 0)
            {
                nextX = 0;
                directionX *= -1;
            }
    
            if ( nextX + getSize().width > parent.getSize().width)
            {
                nextX = parent.getSize().width - getSize().width;
                directionX *= -1;
            }
    
            //  Determine next Y position
    
            int nextY = getLocation().y + (deltaY * directionY);
    
            if (nextY < 0)
            {
                nextY = 0;
                directionY *= -1;
            }
    
            if ( nextY + getSize().height > parent.getSize().height)
            {
                nextY = parent.getSize().height - getSize().height;
                directionY *= -1;
            }
    
            //  Move the label
    
            setLocation(nextX, nextY);
        }
    
        public static void main(String[] args)
        {
            JPanel panel = new JPanel();
            JFrame frame = new JFrame();
    
            frame.setContentPane(panel);
            frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
            frame.getContentPane().setLayout(null);
    //      frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
            frame.getContentPane().add( new TimerAnimation(300, 100, 3, 2, -1, 1, 20) );
    //      frame.getContentPane().add( new TimerAnimation(0, 000, 5, 0, 1, 1, 20) );
            frame.getContentPane().add( new TimerAnimation(0, 200, 5, 0, 1, 1, 80) );
            frame.setSize(400, 400);
            frame.setLocationRelativeTo( null );
            frame.setVisible(true);
    //      frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
    //      frame.getContentPane().add( new TimerAnimation(10, 10, 3, 0, 1, 1, 10) );
        }
    }
    

    You can add a KeyListener to the panel and it will operate independently of the image animation.

    0 讨论(0)
  • 2020-11-22 10:16

    Yep, a Swing Timer and Key Bindings would work well. Here's another example (mine) :)

    import java.awt.*;
    import java.awt.event.*;
    import java.awt.image.BufferedImage;
    import javax.swing.*;
    
    public class AnimationWithKeyBinding {
       private static void createAndShowUI() {
          AnimationPanel panel = new AnimationPanel(); // the drawing JPanel
    
          JFrame frame = new JFrame("Animation With Key Binding");
          frame.getContentPane().add(panel);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.pack();
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          java.awt.EventQueue.invokeLater(new Runnable() {
             public void run() {
                createAndShowUI();
             }
          });
       }
    }
    
    @SuppressWarnings("serial")
    class AnimationPanel extends JPanel {
       public static final int SPRITE_WIDTH = 20;
       public static final int PANEL_WIDTH = 400;
       public static final int PANEL_HEIGHT = 400;
       private static final int MAX_MSTATE = 25;
       private static final int SPIN_TIMER_PERIOD = 16;
       private static final int SPRITE_STEP = 3;
    
       private int mState = 0;
       private int mX = (PANEL_WIDTH - SPRITE_WIDTH) / 2;
       private int mY = (PANEL_HEIGHT - SPRITE_WIDTH) / 2;
       private int oldMX = mX;
       private int oldMY = mY;
       private boolean moved = false;
    
       // an array of sprite images that are drawn sequentially
       private BufferedImage[] spriteImages = new BufferedImage[MAX_MSTATE];
    
       public AnimationPanel() {
          // create and start the main animation timer
          new Timer(SPIN_TIMER_PERIOD, new SpinTimerListener()).start();
          setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
          setBackground(Color.white);
          createSprites(); // create the images
          setupKeyBinding();
       }
    
       private void setupKeyBinding() {
          int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
          InputMap inMap = getInputMap(condition);
          ActionMap actMap = getActionMap();
    
          // this uses an enum of Direction that holds ints for the arrow keys
          for (Direction direction : Direction.values()) {
             int key = direction.getKey();
             String name = direction.name();
    
             // add the key bindings for arrow key and shift-arrow key
             inMap.put(KeyStroke.getKeyStroke(key, 0), name);
             inMap.put(KeyStroke.getKeyStroke(key, InputEvent.SHIFT_DOWN_MASK), name);
             actMap.put(name, new MyKeyAction(this, direction));
          }
       }
    
       // create a bunch of buffered images and place into an array,
       // to be displayed sequentially
       private void createSprites() {
          for (int i = 0; i < spriteImages.length; i++) {
             spriteImages[i] = new BufferedImage(SPRITE_WIDTH, SPRITE_WIDTH,
                      BufferedImage.TYPE_INT_ARGB);
             Graphics2D g2 = spriteImages[i].createGraphics();
             g2.setColor(Color.red);
             g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
             double theta = i * Math.PI / (2 * spriteImages.length);
             double x = SPRITE_WIDTH * Math.abs(Math.cos(theta)) / 2.0;
             double y = SPRITE_WIDTH * Math.abs(Math.sin(theta)) / 2.0;
             int x1 = (int) ((SPRITE_WIDTH / 2.0) - x);
             int y1 = (int) ((SPRITE_WIDTH / 2.0) - y);
             int x2 = (int) ((SPRITE_WIDTH / 2.0) + x);
             int y2 = (int) ((SPRITE_WIDTH / 2.0) + y);
             g2.drawLine(x1, y1, x2, y2);
             g2.drawLine(y1, x2, y2, x1);
             g2.dispose();
          }
       }
    
       @Override
       protected void paintComponent(Graphics g) {
          super.paintComponent(g);
          g.drawImage(spriteImages[mState], mX, mY, null);
       }
    
       public void incrementX(boolean right) {
          oldMX = mX;
          if (right) {
             mX = Math.min(getWidth() - SPRITE_WIDTH, mX + SPRITE_STEP);
          } else {
             mX = Math.max(0, mX - SPRITE_STEP);
          }
          moved = true;
       }
    
       public void incrementY(boolean down) {
          oldMY = mY;
          if (down) {
             mY = Math.min(getHeight() - SPRITE_WIDTH, mY + SPRITE_STEP);
          } else {
             mY = Math.max(0, mY - SPRITE_STEP);
          }
          moved = true;
       }
    
       public void tick() {
          mState = (mState + 1) % MAX_MSTATE;
       }
    
       private class SpinTimerListener implements ActionListener {
          @Override
          public void actionPerformed(ActionEvent e) {
             tick();
    
             int delta = 20;
             int width = SPRITE_WIDTH + 2 * delta;
             int height = width;
    
             // make sure to erase the old image
             if (moved) {
                int x = oldMX - delta;
                int y = oldMY - delta;
                repaint(x, y, width, height);
             }
    
             int x = mX - delta;
             int y = mY - delta;
    
             // draw the new image
             repaint(x, y, width, height);
             moved = false;
          }
       }
    }
    
    enum Direction {
       UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(KeyEvent.VK_RIGHT);
    
       private int key;
    
       private Direction(int key) {
          this.key = key;
       }
    
       public int getKey() {
          return key;
       }
    }
    
    // Actions for the key binding
    @SuppressWarnings("serial")
    class MyKeyAction extends AbstractAction {
       private AnimationPanel draw;
       private Direction direction;
    
       public MyKeyAction(AnimationPanel draw, Direction direction) {
          this.draw = draw;
          this.direction = direction;
       }
    
       @Override
       public void actionPerformed(ActionEvent e) {
          switch (direction) {
          case UP:
             draw.incrementY(false);
             break;
          case DOWN:
             draw.incrementY(true);
             break;
          case LEFT:
             draw.incrementX(false);
             break;
          case RIGHT:
             draw.incrementX(true);
             break;
    
          default:
             break;
          }
       }
    }
    



    Here is another example that uses this sprite sheet:

    obtained from this site.

    Again it's an example of drawing within a JPanel's paintComponent method and using Key Bindings to tell which direction to move.

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.event.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.EnumMap;
    import java.util.List;
    import java.util.Map;
    
    import javax.imageio.ImageIO;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class Mcve3 extends JPanel {
        private static final int PREF_W = 800;
        private static final int PREF_H = 640;
        private static final int TIMER_DELAY = 50;
    
        private int spriteX = 400;
        private int spriteY = 320;
        private SpriteDirection spriteDirection = SpriteDirection.RIGHT;
        private MySprite sprite = null;
        private Timer timer = null;
    
        public Mcve3() {
            try {
                sprite = new MySprite(spriteDirection, spriteX, spriteY);
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(-1);
            }
            setBackground(Color.WHITE);
    
            setKeyBindings(SpriteDirection.LEFT, KeyEvent.VK_LEFT);
            setKeyBindings(SpriteDirection.RIGHT, KeyEvent.VK_RIGHT);
            setKeyBindings(SpriteDirection.FORWARD, KeyEvent.VK_DOWN);
            setKeyBindings(SpriteDirection.AWAY, KeyEvent.VK_UP);
    
            timer = new Timer(TIMER_DELAY, new TimerListener());
            timer.start();
        }
    
        private void setKeyBindings(SpriteDirection dir, int keyCode) {
            int condition = WHEN_IN_FOCUSED_WINDOW;
            InputMap inputMap = getInputMap(condition);
            ActionMap actionMap = getActionMap();
    
            KeyStroke keyPressed = KeyStroke.getKeyStroke(keyCode, 0, false);
            KeyStroke keyReleased = KeyStroke.getKeyStroke(keyCode, 0, true);
    
            inputMap.put(keyPressed, keyPressed.toString());
            inputMap.put(keyReleased, keyReleased.toString());
    
            actionMap.put(keyPressed.toString(), new MoveAction(dir, false));
            actionMap.put(keyReleased.toString(), new MoveAction(dir, true));
        }
    
        @Override
        public Dimension getPreferredSize() {
            if (isPreferredSizeSet()) {
                return super.getPreferredSize();
            }
            return new Dimension(PREF_W, PREF_H);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            sprite.draw(g);
        }
    
        private class MoveAction extends AbstractAction {
            private SpriteDirection dir;
            private boolean released;
    
            public MoveAction(SpriteDirection dir, boolean released) {
                this.dir = dir;
                this.released = released;
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                if (released) {
                    sprite.setMoving(false);
                } else {
                    sprite.setMoving(true);
                    sprite.setDirection(dir);
                }
            }
        }
    
        private class TimerListener implements ActionListener {
            @Override
                public void actionPerformed(ActionEvent e) {
                    if (sprite.isMoving()) {
                        sprite.tick();
                    }
                    repaint();
                }
        }
    
        private static void createAndShowGui() {
            Mcve3 mainPanel = new Mcve3();
    
            JFrame frame = new JFrame("MCVE");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }
    }
    

    class MySprite {
        private static final String SPRITE_SHEET_PATH = "http://"
                + "orig12.deviantart.net/7db3/f/2010/338/3/3/"
                + "animated_sprite_sheet_32x32_by_digibody-d3479l2.gif";
        private static final int MAX_MOVING_INDEX = 4;
        private static final int DELTA = 4;
        private SpriteDirection direction;
        private Map<SpriteDirection, Image> standingImgMap = new EnumMap<>(SpriteDirection.class);
        private Map<SpriteDirection, List<Image>> movingImgMap = new EnumMap<>(SpriteDirection.class);
        private int x;
        private int y;
        private boolean moving = false;
        private int movingIndex = 0;
    
        public MySprite(SpriteDirection direction, int x, int y) throws IOException {
            this.direction = direction;
            this.x = x;
            this.y = y;
            createSprites();
        }
    
        public void draw(Graphics g) {
            Image img = null;
            if (!moving) {
                img = standingImgMap.get(direction);
            } else {
                img = movingImgMap.get(direction).get(movingIndex);
            }
            g.drawImage(img, x, y, null);
        }
    
        private void createSprites() throws IOException {
            URL spriteSheetUrl = new URL(SPRITE_SHEET_PATH);
            BufferedImage img = ImageIO.read(spriteSheetUrl);
    
            // get sub-images (sprites) from the sprite sheet
            // magic numbers for getting sprites from sheet, all obtained by trial and error
            int x0 = 0;
            int y0 = 64;
            int rW = 32;
            int rH = 32;
            for (int row = 0; row < 4; row++) {
                SpriteDirection dir = SpriteDirection.values()[row];
                List<Image> imgList = new ArrayList<>();
                movingImgMap.put(dir, imgList);
                int rY = y0 + row * rH;
                for (int col = 0; col < 5; col++) {
                    int rX = x0 + col * rW;
                    BufferedImage subImg = img.getSubimage(rX, rY, rW, rH);
                    if (col == 0) {
                        // first image is standing
                        standingImgMap.put(dir, subImg);
                    } else {
                        // all others are moving
                        imgList.add(subImg);
                    }
                }
            }
        }
    
        public SpriteDirection getDirection() {
            return direction;
        }
    
        public void setDirection(SpriteDirection direction) {
            if (this.direction != direction) {
                setMoving(false);
            }
            this.direction = direction;
    
        }
    
        public int getX() {
            return x;
        }
    
        public void setX(int x) {
            this.x = x;
        }
    
        public int getY() {
            return y;
        }
    
        public void setY(int y) {
            this.y = y;
        }
    
        public boolean isMoving() {
            return moving;
        }
    
        public void setMoving(boolean moving) {
            this.moving = moving;
            if (!moving) {
                movingIndex = 0;
            }
        }
    
        public void tick() {
            if (moving) {
                switch (direction) {
                case RIGHT:
                    x += DELTA;
                    break;
                case LEFT:
                    x -= DELTA;
                    break;
                case FORWARD:
                    y += DELTA;
                    break;
                case AWAY:
                    y -= DELTA;
                }
                movingIndex++;
                movingIndex %= MAX_MOVING_INDEX;
            }
        }
    
        public int getMovingIndex() {
            return movingIndex;
        }
    
        public void setMovingIndex(int movingIndex) {
            this.movingIndex = movingIndex;
        }
    
    }
    

    enum SpriteDirection {
        FORWARD, LEFT, AWAY, RIGHT
    }
    
    0 讨论(0)
提交回复
热议问题