Java Swing — Key Input with JPanel added to JOptionpane

后端 未结 1 1861
有刺的猬
有刺的猬 2020-12-12 06:36

When I run the code, the added Example1 class to the JOptionPane (in Frame) should get keyInput and then change the y value of the player instance (in example1), but it does

相关标签:
1条回答
  • 2020-12-12 07:16

    KeyListener is well known for having focus related issues. Not only does the component need to be focusable, but it has to have keyboard focus before it will register key events. This is an issue as it's all to easy for your component to lose focus for any number of reasons.

    The solution is to use the key bindings API which was designed to help solve this issue. See How to Use Key Bindings for more details.

    Swing uses a passive rendering approach. That is, updates occur when ever the API decides something needs to be updated. See Painting in AWT and Swing for more details

    What you really need is some kind of active rendering approach, where updates are made on a regular bases. This can be accomplished in a verity of ways, the simplest would be to use a Swing Timer, as it's safe to update the UI (or values that the UI relies on) without the risk of introducing race conditions. See How to use Swing Timers for more details

    So, this example basically abstracts the input into four basic movements, rotate left/right and up/down. The code doesn't care how those inputs are generated, only that they can be.

    It then uses the key bindings API to register actions for the inputs. This is all done with a single Action class, which just adds the Input to a Set, which the Timer uses to determine what actions should be applied before requesting the UI be repainted.

    The rotation is accomplished through the use of a AffineTransform. First we translate the origin to the sprites current x/y position, then rotate it around the center of the sprite. This reduces a lot of the complexity/issues related to rotating things (me simple)

    AffineTransform at = AffineTransform.getTranslateInstance(xPos, yPos);
    at.rotate(Math.toRadians(angle), sprite.getWidth() / 2, sprite.getHeight() / 2);
    g2d.setTransform(at);
    g2d.drawImage(sprite, 0, 0, this);
    

    Just beware, a Graphics context is a shared resource, before making these type of changes, you need to make a copy of it (and dispose of it when you are finished)

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    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.IOException;
    import java.util.HashSet;
    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 FlyingPoniesWithGuns {
    
        public static void main(String[] args) {
            new FlyingPoniesWithGuns();
        }
    
        public FlyingPoniesWithGuns() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        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 TestPane());
                        frame.pack();
                        frame.setLocationRelativeTo(null);
                        frame.setVisible(true);
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                }
            });
        }
    
        public enum Input {
            ROTATE_LEFT,
            ROTATE_RIGHT,
            UP,
            DOWN
        }
    
        public class TestPane extends JPanel {
    
            private BufferedImage sprite;
            private double angle;
            private int xPos, yPos;
            private double xDelta, yDelta;
    
            private Set<Input> inputs;
    
            public TestPane() throws IOException {
                inputs = new HashSet<>(25);
                sprite = ImageIO.read(getClass().getResource("/Pony.png"));
                xPos = (400 - sprite.getWidth()) / 2;
                yPos = (400 - sprite.getHeight()) / 2;
    
                addKeyBinding("rotate-left", KeyEvent.VK_A, Input.ROTATE_LEFT);
                addKeyBinding("rotate-right", KeyEvent.VK_D, Input.ROTATE_RIGHT);
    
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (inputs.contains(Input.ROTATE_LEFT)) {
                            angle -= 5;
                        } else if (inputs.contains(Input.ROTATE_RIGHT)) {
                            angle += 5;
                        }
                        repaint();
                    }
                });
                timer.start();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(400, 400);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
                g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
                g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
    
                AffineTransform at = AffineTransform.getTranslateInstance(xPos, yPos);
                at.rotate(Math.toRadians(angle), sprite.getWidth() / 2, sprite.getHeight() / 2);
                g2d.setTransform(at);
                g2d.drawImage(sprite, 0, 0, this);
                g2d.dispose();
            }
    
            protected void addKeyBinding(String name, int keyCode, Input input) {
                InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
                ActionMap actionMap = getActionMap();
    
                inputMap.put(KeyStroke.getKeyStroke(keyCode, 0, false), name + ".pressed");
                actionMap.put(name + ".pressed", new InputAction(input, true));
    
                inputMap.put(KeyStroke.getKeyStroke(keyCode, 0, true), name + ".released");
                actionMap.put(name + ".released", new InputAction(input, false));
            }
    
            protected class InputAction extends AbstractAction {
    
                private Input input;
                private boolean pressed;
    
                public InputAction(Input input, boolean pressed) {
                    this.input = input;
                    this.pressed = pressed;
                }
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (pressed) {
                        inputs.add(input);
                    } else {
                        inputs.remove(input);
                    }
                }
    
            }
    
        }
    
    }
    

    Okay, what about movement? You could have a look at How do I make an entity move in a direction? and How can I move a sprite in the direction it is facing? for more ideas on how you might be able to achieve that

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