How to move Polygon Object with KeyListener in Jav

2020-02-16 04:11发布

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 added to a JFrame). I've tried the frog.translate(int x, int y) method, which does not update the location. I've also tried changing the array coordinates manually. A sample of my code is below:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Board extends JPanel implements KeyListener {

    private Frog frog;

    public Board() {
        setBackground(Color.GREEN);
        addKeyListener(this);
        setFocusable(true);
        setFocusTraversalKeysEnabled(false);
    }

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

    @Override
    public void keyTyped(KeyEvent ke) {
    }

    @Override
    public void keyPressed(KeyEvent ke) {
        int c = ke.getKeyCode();
        if(c == KeyEvent.VK_LEFT){
            frog.moveFrogLeft(25);
            //frog.translate(-25,0);
        }

        if(c == KeyEvent.VK_RIGHT){
            frog.moveFrogRight(25);
            //frog.translate(25,0);
        }

        if(c == KeyEvent.VK_UP){
            frog.moveFrogUp(25);
            //frog.translate(0,-25);
        }

        if(c == KeyEvent.VK_DOWN){
            frog.moveFrogDown(25);
            //frog.translate(0,25);
        }
        repaint();
     }

     @Override
     public void keyReleased(KeyEvent ke) {
     } 
 }

///////////////////////

import java.awt.Polygon;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Frog extends Polygon {

    private Integer[] xcoord;
    private Integer[] ycoord;

    public Frog(){

        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 void moveFrogLeft(int x) {
        if(xcoord[0] - x < 0){
            //do nothing
        } else {
            for(int i = 0; i < xcoord.length; i++){
                xcoord[i] = xcoord[i] - x;
            }
        }
    }

    public void moveFrogRight(int x){
        if(xcoord[0] + x > 600){
            //do nothing
        } else {
            for(int i = 0; i < xcoord.length; i++){
                xcoord[i] = xcoord[i] + x;
            }
        }
    }

    public void moveFrogUp(int y){
        if(ycoord[0] - y < 0){
            //do nothing
        } else {
            for(int i = 0; i < ycoord.length; i++){
                ycoord[i] = ycoord[i] - y;
            }
        }
    }

    public void moveFrogDown(int y){
        if(ycoord[0] + y > 600){
            //do nothing
        } else {
            for(int i = 0; i < ycoord.length; i++){
                ycoord[i] = ycoord[i] + y;
            }
        }
    }
}

3条回答
爷的心禁止访问
2楼-- · 2020-02-16 04:41

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!!!

查看更多
够拽才男人
3楼-- · 2020-02-16 04:47

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".

查看更多
迷人小祖宗
4楼-- · 2020-02-16 04:59

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...

Frog

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!

查看更多
登录 后发表回答