How to move Polygon Object with KeyListener in Java

后端 未结 3 1632
闹比i
闹比i 2020-12-22 06:32

I am working on a 2D game as a learning project and I have hit a bump. I cannot figure out how to move a Polygon object using the KeyListener within a JPanel (which is adde

相关标签:
3条回答
  • 2020-12-22 06:34

    First of all, I would strongly discourage you from using KeyListener, it's troublesome at the best, a better choice would be to make use of the Key Bindings API which was desgiend to fix the short commings of the KeyListener API.

    Second, you shouldn't be modifying the points of the polygon, the 2D Graphics API is actually capable of some really neat tricks which makes it much easier and faster to change the location (and rotation and scale) of what you are drawing.

    Take a look closer look at 2D Graphics for more details.

    Instead of changing the points of the polygon, which isn't taking into consideration the viewable bounds, you could simply use an AffineTransform...

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        Point location = frog.getLocation();
        AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y);
        g2d.transform(at);
        g2d.setColor(new Color(0, 150, 15));
        g2d.fill(frog);
        g2d.setColor(Color.BLACK);
        g2d.draw(frog);
        g2d.dispose();
    }
    

    This just simply changes the origin point of the Graphics context to the location where you want to paint the polygon (yes, there's another reason why I'm using AffineTransform

    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.Polygon;
    import java.awt.Rectangle;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyEvent;
    import java.awt.geom.AffineTransform;
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    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 Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            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 Board());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        protected enum VerticalDirection {
            NONE, UP, DOWN;
        }
    
        protected enum HorizontalDirection {
            NONE, LEFT, RIGHT;
        }
    
        public static class Board extends JPanel {
    
            protected static final int Y_DELTA = 4;
            protected static final int X_DELTA = 4;
    
            private Frog frog;
            private VerticalDirection verticalDirection = VerticalDirection.NONE;
            private HorizontalDirection horizontalDirection = HorizontalDirection.NONE;
    
            public Board() {
                setBackground(Color.GREEN);
                frog = new Frog();
    
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Point location = frog.getLocation();
                        switch (verticalDirection) {
                            case UP:
                                location.y -= Y_DELTA;
                                break;
                            case DOWN:
                                location.y += Y_DELTA;
                                break;
                        }
                        switch (horizontalDirection) {
                            case LEFT:
                                location.x -= X_DELTA;
                                break;
                            case RIGHT:
                                location.x += X_DELTA;
                                break;
                        }
    
                        Rectangle bounds = frog.getBounds();
                        int width = bounds.x + bounds.width;
                        int height = bounds.y + bounds.height;
                        if (location.y < 0) {
                            location.y = 0;
                        } else if (location.y + height > getHeight()) {
                            location.y = getHeight() - height;
                        }
                        if (location.x < 0) {
                            location.x = 0;
                        } else if (location.x + width > getWidth()) {
                            location.x = getWidth() - width;
                        }
                        frog.setLocation(location);
                        repaint();
                    }
                });
                timer.start();
    
                addPressedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.UP));
                addPressedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.DOWN));
                addPressedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.LEFT));
                addPressedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.RIGHT));
    
                addReleasedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.NONE));
                addReleasedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.NONE));
                addReleasedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.NONE));
                addReleasedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.NONE));
            }
    
            protected void addPressedKeyBinding(String name, int virtuaKey, Action action) {
                addKeyBinding(name + ".pressed", KeyStroke.getKeyStroke(virtuaKey, 0, false), action);
            }
    
            protected void addReleasedKeyBinding(String name, int virtuaKey, Action action) {
                addKeyBinding(name + ".released", KeyStroke.getKeyStroke(virtuaKey, 0, true), action);
            }
    
            protected void addKeyBinding(String name, KeyStroke ks, Action action) {
                InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
                ActionMap am = getActionMap();
    
                im.put(ks, name);
                am.put(name, action);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                Point location = frog.getLocation();
                AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y);
                g2d.transform(at);
                g2d.setColor(new Color(0, 150, 15));
                g2d.fill(frog);
                g2d.setColor(Color.BLACK);
                g2d.draw(frog);
                g2d.dispose();
            }
    
            protected class VerticalMovementAction extends AbstractAction {
    
                private VerticalDirection direction;
    
                public VerticalMovementAction(VerticalDirection direction) {
                    this.direction = direction;
                }
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    verticalDirection = direction;
                }
    
            }
    
            protected class HorizontalMovementAction extends AbstractAction {
    
                private HorizontalDirection direction;
    
                public HorizontalMovementAction(HorizontalDirection direction) {
                    this.direction = direction;
                }
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    horizontalDirection = direction;
                }
    
            }
    
        }
    
        public static class Frog extends Polygon {
    
            private Integer[] xcoord;
            private Integer[] ycoord;
    
            private Point location;
    
            public Frog() {
    
                location = new Point(0, 0);
    
                xcoord = new Integer[]{5, 10, 10, 15, 15, 20,
                    20, 30, 30, 35, 35, 40, 40,
                    45, 45, 40, 40, 30, 30, 40,
                    40, 45, 45, 40, 40, 35, 35,
                    30, 30, 20, 20, 15, 15, 10,
                    10, 5, 5, 10, 10, 20, 20,
                    10, 10, 5, 5};
    
                ycoord = new Integer[]{10, 10, 5, 5, 20, 20,
                    10, 10, 20, 20, 5, 5, 10, 10,
                    15, 15, 25, 25, 30, 30, 35, 35,
                    40, 40, 45, 45, 35, 35, 40, 40,
                    35, 35, 45, 45, 40, 40, 35, 35,
                    30, 30, 25, 25, 15, 15, 10};
    
                for (int i = 0; i < xcoord.length; i++) {
                    this.addPoint(xcoord[i], ycoord[i]);
                }
            }
    
            public Point getLocation() {
                return location;
            }
    
            public void setLocation(Point location) {
                this.location = location;
            }
    
        }
    
    }
    

    Now, you're probably wondering why I would use AffineTransform instead of Graphcis2D#translate, the main reason is, it's easy to apply other transformations, like rotation...

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        Point location = frog.getLocation();
    
        Rectangle bounds = frog.getBounds();
        int width = bounds.x + bounds.width;
        int height = bounds.y + bounds.height;
        AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y);
        at.rotate(Math.toRadians(angle), width / 2, height / 2);
        g2d.transform(at);
    
        g2d.setColor(new Color(0, 150, 15));
        g2d.fill(frog);
        g2d.setColor(Color.BLACK);
        g2d.draw(frog);
        g2d.dispose();
    }
    

    All this does is apply a compound transformation, moving the Graphics context's origin and rotation matrix

    And for a complete example...

    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.Polygon;
    import java.awt.Rectangle;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyEvent;
    import java.awt.geom.AffineTransform;
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    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 Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            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 Board());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        protected enum VerticalDirection {
            NONE, UP, DOWN;
        }
    
        protected enum HorizontalDirection {
            NONE, LEFT, RIGHT;
        }
    
        public static class Board extends JPanel {
    
            protected static final int Y_DELTA = 4;
            protected static final int X_DELTA = 4;
    
            private Frog frog;
            private VerticalDirection verticalDirection = VerticalDirection.NONE;
            private HorizontalDirection horizontalDirection = HorizontalDirection.NONE;
    
            private double angle = 0; // Up...
    
            public Board() {
                setBackground(Color.GREEN);
                frog = new Frog();
    
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Point location = frog.getLocation();
                        switch (verticalDirection) {
                            case UP:
                                angle = 0;
                                location.y -= Y_DELTA;
                                break;
                            case DOWN:
                                angle = 180;
                                location.y += Y_DELTA;
                                break;
                        }
                        switch (horizontalDirection) {
                            case LEFT:
                                location.x -= X_DELTA;
                                angle = 270;
                                break;
                            case RIGHT:
                                location.x += X_DELTA;
                                angle = 90;
                                break;
                        }
    
                        Rectangle bounds = frog.getBounds();
                        int width = bounds.x + bounds.width;
                        int height = bounds.y + bounds.height;
                        if (location.y < 0) {
                            location.y = 0;
                        } else if (location.y + height > getHeight()) {
                            location.y = getHeight() - height;
                        }
                        if (location.x < 0) {
                            location.x = 0;
                        } else if (location.x + width > getWidth()) {
                            location.x = getWidth() - width;
                        }
                        frog.setLocation(location);
                        repaint();
                    }
                });
                timer.start();
    
                addPressedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.UP));
                addPressedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.DOWN));
                addPressedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.LEFT));
                addPressedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.RIGHT));
    
                addReleasedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.NONE));
                addReleasedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.NONE));
                addReleasedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.NONE));
                addReleasedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.NONE));
            }
    
            protected void addPressedKeyBinding(String name, int virtuaKey, Action action) {
                addKeyBinding(name + ".pressed", KeyStroke.getKeyStroke(virtuaKey, 0, false), action);
            }
    
            protected void addReleasedKeyBinding(String name, int virtuaKey, Action action) {
                addKeyBinding(name + ".released", KeyStroke.getKeyStroke(virtuaKey, 0, true), action);
            }
    
            protected void addKeyBinding(String name, KeyStroke ks, Action action) {
                InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
                ActionMap am = getActionMap();
    
                im.put(ks, name);
                am.put(name, action);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                Point location = frog.getLocation();
    
                Rectangle bounds = frog.getBounds();
                int width = bounds.x + bounds.width;
                int height = bounds.y + bounds.height;
                AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y);
                at.rotate(Math.toRadians(angle), width / 2, height / 2);
                g2d.transform(at);
    
                g2d.setColor(new Color(0, 150, 15));
                g2d.fill(frog);
                g2d.setColor(Color.BLACK);
                g2d.draw(frog);
                g2d.dispose();
            }
    
            protected class VerticalMovementAction extends AbstractAction {
    
                private VerticalDirection direction;
    
                public VerticalMovementAction(VerticalDirection direction) {
                    this.direction = direction;
                }
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    verticalDirection = direction;
                }
    
            }
    
            protected class HorizontalMovementAction extends AbstractAction {
    
                private HorizontalDirection direction;
    
                public HorizontalMovementAction(HorizontalDirection direction) {
                    this.direction = direction;
                }
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    horizontalDirection = direction;
                }
    
            }
    
        }
    
        public static class Frog extends Polygon {
    
            private Integer[] xcoord;
            private Integer[] ycoord;
    
            private Point location;
    
            public Frog() {
    
                location = new Point(0, 0);
    
                xcoord = new Integer[]{5, 10, 10, 15, 15, 20,
                    20, 30, 30, 35, 35, 40, 40,
                    45, 45, 40, 40, 30, 30, 40,
                    40, 45, 45, 40, 40, 35, 35,
                    30, 30, 20, 20, 15, 15, 10,
                    10, 5, 5, 10, 10, 20, 20,
                    10, 10, 5, 5};
    
                ycoord = new Integer[]{10, 10, 5, 5, 20, 20,
                    10, 10, 20, 20, 5, 5, 10, 10,
                    15, 15, 25, 25, 30, 30, 35, 35,
                    40, 40, 45, 45, 35, 35, 40, 40,
                    35, 35, 45, 45, 40, 40, 35, 35,
                    30, 30, 25, 25, 15, 15, 10};
    
                // I rest the coordinates back to 0x0 because it's easier to 
                // deal with when applying a rotation...
                for (int index = 0; index < xcoord.length; index++) {
                    xcoord[index] -= 5;
                }
                for (int index = 0; index < ycoord.length; index++) {
                    ycoord[index] -= 5;
                }
    
                for (int i = 0; i < xcoord.length; i++) {
                    this.addPoint(xcoord[i], ycoord[i]);
                }
            }
    
            public Point getLocation() {
                return location;
            }
    
            public void setLocation(Point location) {
                this.location = location;
            }
    
        }
    
    }
    

    Look ma, no maths!

    0 讨论(0)
  • 2020-12-22 06:38

    This code has a simple issue:

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;        
    
        frog = new Frog();// <-- !!!!!
    
        // Frog graphics
        g2.setColor(Color.BLACK);
        g2.drawPolygon(frog);
        g2.setColor(new Color(0,150,15));
        g2.fillPolygon(frog);
    }
    

    The marked line overwrites the frog with a new instance, every time the frog is painted, thus reseting it to the original point. Apart from the obvious issue that this is the reason for the unexpected behaviour, never do any unnecessary calculations in the paintComponent(...)-method. Any precomputation, Object-generation, etc. should be done outside of paintComponent!!!

    0 讨论(0)
  • 2020-12-22 06:51

    Don't create a Frog in your paintComponent() method! That is throwing away the existing frog and creating a new one with default position. You should create all of your Frog instances when you initialize your panel, or possibly in response to a b button click to "create a new frog".

    0 讨论(0)
提交回复
热议问题