Java Swing – 将JPanel添加到JOptionpane的键输入

当我运行代码时,添加的Example1类到JOptionPane(在Frame中)应该获得keyInput,然后更改播放器实例的y值(在example1中),但它不起作用。 另外,我如何能够在其轴上旋转船舶,然后朝着其面向的方向移动? 目前它沿着其旋转的方向移动,但它在坐标0,0上旋转。

import javax.swing.*; import java.awt.*; /** * Created by griffin on 12/7/2015. */ public class Frame extends JFrame { public Frame() { initUI(); } private void initUI() { JTabbedPane jtp = new JTabbedPane(); Example1 e1 = new Example1(); Example2 e2 = new Example2(); getContentPane().add(jtp); jtp.add(e1); jtp.add(e2); jtp.addTab("Example 1", e1); jtp.addTab("Example 2", e2); setResizable(false); pack(); setTitle("NLTP"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame frame = new Frame(); frame.setVisible(true); } }); } } 

例1

 import javax.swing.*; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.geom.AffineTransform; /** * Created by griffin on 12/7/2015. */ public class Example1 extends JPanel implements Runnable { private final int B_WIDTH = 640; private final int B_HEIGHT = 480; private int DELAY = 25; private Thread thread; Ship player = new Ship(320, 240); public Example1() { initScreen(); } private void initScreen() { JButton explanation = new JButton("Explanation"); explanation.setBounds(0, 0, 150, 30); this.setLayout(null); add(explanation); addKeyListener(new keyInput()); setFocusable(true); setBackground(Color.BLACK); setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT)); setDoubleBuffered(true); } @Override public void addNotify() { super.addNotify(); thread = new Thread(this); thread.start(); } public void paintComponent(Graphics g) { super.paintComponent(g); draw(g); } public void draw(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; AffineTransform old = g2d.getTransform(); g2d.rotate(Math.toRadians(player.angle)); g2d.drawImage(player.ship, player.x, player.y, null); g2d.setTransform(old); Toolkit.getDefaultToolkit().sync(); } public void cycle() { player.y += player.vY; } @Override public void run() { long beforeTime, timeDiff, sleep; beforeTime = System.currentTimeMillis(); while (true) { cycle(); repaint(); timeDiff = System.currentTimeMillis() - beforeTime; sleep = DELAY - timeDiff; if (sleep < 0) { sleep = 2; } try { Thread.sleep(sleep); } catch (InterruptedException e) { System.out.println("Interrupted: " + e.getMessage()); } beforeTime = System.currentTimeMillis(); } } public class keyInput extends KeyAdapter { @Override public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if(key == KeyEvent.VK_W) { } } @Override public void keyTyped(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_W) { player.vY -= 1; } if(key == KeyEvent.VK_A) { player.angle++; } if(key == KeyEvent.VK_D) { player.angle--; } } } } 

KeyListener因焦点相关问题而闻名。 组件不仅需要可聚焦,而且在注册关键事件之前必须具有键盘焦点。 这是一个问题,因为由于各种原因,您的组件很容易失去焦点。

解决方案是使用旨在帮助解决此问题的密钥绑定API。 有关更多详细信息,请参见如何使用键绑定 。

Swing使用被动渲染方法。 也就是说,当API决定需要更新某些内容时,就会发生更新。 有关详细信息,请参阅AWT和Swing中的绘画

你真正需要的是某种主动渲染方法,其中更新是在常规基础上进行的。 这可以通过多种方式实现,最简单的方法是使用Swing Timer ,因为更新UI(或UI所依赖的值)是安全的,而不存在引入竞争条件的风险。 有关更多详细信息,请参见如何使用Swing Timers

因此,这个例子基本上将输入抽象为四个基本动作,左/右和上/下旋转。 代码并不关心这些输入是如何生成的,只是它们可以生成。

然后,它使用键绑定API来注册输入的操作。 这一切都是通过一个Action类完成的,它只是将一个Input添加到一个SetTimer在确定重新绘制UI之前用它来确定应该应用哪些动作。

通过使用AffineTransform完成旋转。 首先,我们将原点转换为精灵当前的x / y位置,然后围绕精灵的中心旋转它。 这减少了很多与旋转物相关的复杂性/问题(我很简单)

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

请注意, Graphics上下文是共享资源,在进行这些类型的更改之前,您需要复制它(并在完成后处理它)

小马

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

运动怎么样? 你可以看一下如何让一个实体向一个方向移动? 如何沿着它面向的方向移动精灵? 有关如何实现这一目标的更多想法