Java 如何使用 KeyListener 移动多边形对象
How to move Polygon Object with KeyListener in Java
作为一个学习项目,我正在开发一款 2D 游戏,但遇到了一些困难。我不知道如何使用 JPanel(添加到 JFrame)中的 KeyListener 移动多边形对象。我试过 frog.translate(int x, int y) 方法,它不更新位置。我也试过手动更改数组坐标。我的代码示例如下:
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;
}
}
}
}
这段代码有一个简单的问题:
@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
!!!
之外完成
不要在 paintComponent() 方法中创建青蛙!那就是扔掉现有的青蛙并创建一个具有默认位置的新青蛙。
您应该在初始化面板时创建所有 Frog 实例,或者可能响应 b 按钮单击 "create a new frog"。
首先,我强烈反对你使用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
[=25= 的另一个原因]
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;
}
}
}
你看,妈妈,没有数学!
作为一个学习项目,我正在开发一款 2D 游戏,但遇到了一些困难。我不知道如何使用 JPanel(添加到 JFrame)中的 KeyListener 移动多边形对象。我试过 frog.translate(int x, int y) 方法,它不更新位置。我也试过手动更改数组坐标。我的代码示例如下:
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;
}
}
}
}
这段代码有一个简单的问题:
@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
!!!
不要在 paintComponent() 方法中创建青蛙!那就是扔掉现有的青蛙并创建一个具有默认位置的新青蛙。 您应该在初始化面板时创建所有 Frog 实例,或者可能响应 b 按钮单击 "create a new frog"。
首先,我强烈反对你使用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
[=25= 的另一个原因]
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;
}
}
}
你看,妈妈,没有数学!