如何使用Java中的KeyListener移动Polygon对象

我正在研究2D游戏作为一个学习项目,我遇到了一个问题。 我无法弄清楚如何使用JPanel(添加到JFrame中)中的KeyListener移动Polygon对象。 我尝试过frog.translate(int x,int y)方法,它不会更新位置。 我也试过手动更改arrays坐标。 我的代码示例如下:

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  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  600){ //do nothing } else { for(int i = 0; i < ycoord.length; i++){ ycoord[i] = ycoord[i] + y; } } } } 

这段代码有一个简单的问题:

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

每次青蛙被涂上时,标记的线都会用新的实例覆盖青蛙,从而将其重置到原始点。 除了明显的问题,这是意外行为的原因, 永远不要在paintComponent(...) - 方法中做任何不必要的计算。 任何预计算,对象生成等都应该在paintComponent之外完成!

首先,我强烈反对你不使用KeyListener ,它在最好的情况下很麻烦,更好的选择是使用Key Bindings API来解决KeyListener API的短路问题。

其次,你不应该修改多边形的点,2D图形API实际上能够有一些非常巧妙的技巧,这使得更容易和更快地改变你绘制的位置(以及旋转和缩放)。

有关详细信息,请仔细查看2D Graphics 。

您可以简单地使用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(); } 

这只是简单地将Graphics上下文的原点更改为您想要绘制多边形的位置(是的,这是我使用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; } } } 

现在,您可能想知道为什么我会使用AffineTransform而不是Graphcis2D#translate ,主要原因是,它很容易应用其他转换,如旋转......

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

所有这一切都是应用复合变换,移动Graphics上下文的原点和旋转矩阵

并举一个完整的例子......

青蛙

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

看马,没有数学!

不要在paintComponent()方法中创建Frog! 这就是抛弃现有的青蛙并创建一个具有默认位置的新青蛙。 您应该在初始化面板时创建所有Frog实例,或者可能响应ab按钮单击以“创建新的青蛙”。