围绕其中心旋转移动的形状
Rotate a moving shape around its center
我正在 Java 制作一款 2D 游戏,玩家可以在其中引导多边形穿过障碍物。多边形上下移动,游戏世界左右滚动。我需要多边形围绕其中心旋转,但由于它不断被平移,因此它围绕移动旋转的点。试图将它平移回原来的中心,旋转它,然后再平移回去是行不通的。如何获得形状的中心?
这是我在 2ms 计时器上的运动计算:
@Override
public void actionPerformed(ActionEvent e) {
double theta = angleRad+90;
if (up == true) {
if (accelerating == false) {
time2 = 0;
moveX0 = moveX;
moveY0 = moveY;
accelerating = true;
}
time1++;
double t = time1/500;
if (accCount % 10 == 0) {
DronePilot.velocity++;
}
moveX = moveX0 + velX*Math.cos(theta)*t;
moveY = moveY0 + velY*Math.sin(theta)*t-(1/2d)*g*Math.pow(t, 2);
velX = (DronePilot.velocity)*Math.cos(theta);
velY = (DronePilot.velocity)*Math.sin(theta)-g*(t);
accCount++;
} else if (up == false){
if (accelerating == true) {
time1 = 0;
moveX0 = moveX;
moveY0 = moveY;
accelerating = false;
}
time2++;
double t = time2/500;
moveX = moveX0 + velX*Math.cos(theta)*t;
moveY = moveY0 + velY*Math.sin(theta)*t-(1/2d)*g*Math.pow(t, 2);
accCount = 0;
} if (left == true) {
angleCount++;
if (angleCount % 2 == 0) {
angleDeg++;
}
angleRad = Math.toRadians(angleDeg);
} else if (right == true) {
angleCount--;
if (angleCount % 2 == 0) {
angleDeg--;
}
angleRad = Math.toRadians(angleDeg);
}
repaint();
}
}
这是我的 paintComponent 方法:
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
Graphics g2 = g.create();
Graphics2D copy = (Graphics2D)g2;
copy.rotate(-angleRad, xPos, yPos);
copy.translate(0, -moveY);
g2D.translate(-moveX, 0);
copy.draw(player.shape);
for (Rectangle2D.Double r: DronePilot.rocksFloorArray) {
g2D.draw(r);
}
for (Rectangle2D.Double r: DronePilot.rocksCeilArray) {
g2D.draw(r);
}
for (Rectangle2D.Double r: DronePilot.roomsArray) {
g2D.draw(r);
}
}
其中 (xPos, yPos) 是屏幕的中心。
在 openGL 中,我们使用 pushMatrix()
和 popMatrix()
保存变换状态。这里它们的等价物是 Graphics.create()
和 Graphics.dispose()
Graphics g1 = g.create();
//do transformations
g1.dispose();
Graphics g2 = g.create();
//do other stuff
g2.dispose();
转换(通常)是复合的
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
Graphics g2 = g.create();
Graphics2D copy = (Graphics2D)g2;
copy.rotate(-angleRad, xPos, yPos);
copy.translate(0, -moveY);
g2D.translate(-moveX, 0);
copy.draw(player.shape);
for (Rectangle2D.Double r: DronePilot.rocksFloorArray) {
g2D.draw(r);
}
for (Rectangle2D.Double r: DronePilot.rocksCeilArray) {
g2D.draw(r);
}
for (Rectangle2D.Double r: DronePilot.roomsArray) {
g2D.draw(r);
}
}
在上面的代码中,您同时翻译了原始 Graphics
上下文和 copy
。在这种情况下,copy
不会受到原件的影响,原件也不会受到 copy
的影响,但是,原件上下文是共享资源,并且由于您没有重置翻译,您每次都会继续获得翻译的上下文(复合)。
作为一般经验法则,对副本进行所有转换并在完成后将其丢弃。
例如...
Graphics2D g2d = (Graphics2D)g.create();
AffineTransform at = AffineTransform.getTranslateInstance(playerPoint.x, playerPoint.y);
at.rotate(Math.toRadians(angle), player.getBounds2D().getCenterX(), player.getBounds2D().getCenterY());
g2d.setTransform(at);
g2d.setColor(Color.RED);
g2d.fill(player);
g2d.setColor(Color.BLACK);
g2d.draw(player);
g2d.dispose();
这基本上是将对象的位置转换为玩家的位置,然后围绕对象的中心旋转对象
您还可以应用一个转换,创建该上下文的副本并应用另一个转换,这将变得复合(因此您可以 translate
一个上下文,复制它,然后 rotate
副本和第一个翻译仍适用于副本)
这个令人难以置信的简单示例演示了两个基本示例...
- 使用
Graphics
上下文和 AffineTransform
平移和旋转玩家对象(围绕其中心点)
- 使用
Path2D
生成变换后的形状(此示例生成两个对象,但您可以使用单个 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.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Shape player;
private Point playerPoint;
private float angle;
private float deltaZ = 1.0f;
private int deltaX, deltaY;
public TestPane() {
player = new Rectangle(0, 0, 20, 20);
playerPoint = new Point(80, 80);
Random rnd = new Random();
deltaX = 1;
deltaY = -1;
Timer timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
playerPoint.x += deltaX;
playerPoint.y += deltaY;
Shape rotatedPlayer = rotatedAndTranslatedPlayer();
Rectangle2D bounds = rotatedPlayer.getBounds2D();
if (bounds.getX() < 0.0) {
playerPoint.x = (int)(bounds.getX() * -1);
deltaX *= -1;
} else if (bounds.getX() + bounds.getWidth() >= getWidth()) {
playerPoint.x = getWidth() - (int)bounds.getWidth();
deltaX *= -1;
}
if (bounds.getY() < 0) {
playerPoint.y = 0;
deltaY *= -1;
} else if (bounds.getY() + bounds.getHeight() > getHeight()) {
playerPoint.y = getHeight() - (int)bounds.getHeight();
deltaY *= -1;
}
angle += deltaZ;
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected Shape rotatedAndTranslatedPlayer() {
Path2D.Double rotated = new Path2D.Double(player, AffineTransform.getRotateInstance(
Math.toRadians(angle),
player.getBounds2D().getCenterX(),
player.getBounds2D().getCenterY()));
return new Path2D.Double(rotated, AffineTransform.getTranslateInstance(playerPoint.x, playerPoint.y));
}
// Simply paints the "area" that the player takes up when it's rotated and
// translated
protected void paintAutoTranslatedShape(Graphics2D g2d) {
g2d.setColor(Color.DARK_GRAY);
g2d.fill(rotatedAndTranslatedPlayer().getBounds2D());
}
// Uses a AffineTransform to translate and rotate the player
protected void paintPlayer(Graphics2D g2d) {
AffineTransform at = AffineTransform.getTranslateInstance(playerPoint.x, playerPoint.y);
at.rotate(Math.toRadians(angle), player.getBounds2D().getCenterX(), player.getBounds2D().getCenterY());
g2d.setTransform(at);
g2d.setColor(Color.RED);
g2d.fill(player);
g2d.setColor(Color.BLACK);
g2d.draw(player);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
paintAutoTranslatedShape(g2d);
g2d.dispose();
g2d = (Graphics2D) g.create();
paintPlayer(g2d);
g2d.dispose();
}
}
}
我正在 Java 制作一款 2D 游戏,玩家可以在其中引导多边形穿过障碍物。多边形上下移动,游戏世界左右滚动。我需要多边形围绕其中心旋转,但由于它不断被平移,因此它围绕移动旋转的点。试图将它平移回原来的中心,旋转它,然后再平移回去是行不通的。如何获得形状的中心?
这是我在 2ms 计时器上的运动计算:
@Override
public void actionPerformed(ActionEvent e) {
double theta = angleRad+90;
if (up == true) {
if (accelerating == false) {
time2 = 0;
moveX0 = moveX;
moveY0 = moveY;
accelerating = true;
}
time1++;
double t = time1/500;
if (accCount % 10 == 0) {
DronePilot.velocity++;
}
moveX = moveX0 + velX*Math.cos(theta)*t;
moveY = moveY0 + velY*Math.sin(theta)*t-(1/2d)*g*Math.pow(t, 2);
velX = (DronePilot.velocity)*Math.cos(theta);
velY = (DronePilot.velocity)*Math.sin(theta)-g*(t);
accCount++;
} else if (up == false){
if (accelerating == true) {
time1 = 0;
moveX0 = moveX;
moveY0 = moveY;
accelerating = false;
}
time2++;
double t = time2/500;
moveX = moveX0 + velX*Math.cos(theta)*t;
moveY = moveY0 + velY*Math.sin(theta)*t-(1/2d)*g*Math.pow(t, 2);
accCount = 0;
} if (left == true) {
angleCount++;
if (angleCount % 2 == 0) {
angleDeg++;
}
angleRad = Math.toRadians(angleDeg);
} else if (right == true) {
angleCount--;
if (angleCount % 2 == 0) {
angleDeg--;
}
angleRad = Math.toRadians(angleDeg);
}
repaint();
}
}
这是我的 paintComponent 方法:
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
Graphics g2 = g.create();
Graphics2D copy = (Graphics2D)g2;
copy.rotate(-angleRad, xPos, yPos);
copy.translate(0, -moveY);
g2D.translate(-moveX, 0);
copy.draw(player.shape);
for (Rectangle2D.Double r: DronePilot.rocksFloorArray) {
g2D.draw(r);
}
for (Rectangle2D.Double r: DronePilot.rocksCeilArray) {
g2D.draw(r);
}
for (Rectangle2D.Double r: DronePilot.roomsArray) {
g2D.draw(r);
}
}
其中 (xPos, yPos) 是屏幕的中心。
在 openGL 中,我们使用 pushMatrix()
和 popMatrix()
保存变换状态。这里它们的等价物是 Graphics.create()
和 Graphics.dispose()
Graphics g1 = g.create();
//do transformations
g1.dispose();
Graphics g2 = g.create();
//do other stuff
g2.dispose();
转换(通常)是复合的
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
Graphics g2 = g.create();
Graphics2D copy = (Graphics2D)g2;
copy.rotate(-angleRad, xPos, yPos);
copy.translate(0, -moveY);
g2D.translate(-moveX, 0);
copy.draw(player.shape);
for (Rectangle2D.Double r: DronePilot.rocksFloorArray) {
g2D.draw(r);
}
for (Rectangle2D.Double r: DronePilot.rocksCeilArray) {
g2D.draw(r);
}
for (Rectangle2D.Double r: DronePilot.roomsArray) {
g2D.draw(r);
}
}
在上面的代码中,您同时翻译了原始 Graphics
上下文和 copy
。在这种情况下,copy
不会受到原件的影响,原件也不会受到 copy
的影响,但是,原件上下文是共享资源,并且由于您没有重置翻译,您每次都会继续获得翻译的上下文(复合)。
作为一般经验法则,对副本进行所有转换并在完成后将其丢弃。
例如...
Graphics2D g2d = (Graphics2D)g.create();
AffineTransform at = AffineTransform.getTranslateInstance(playerPoint.x, playerPoint.y);
at.rotate(Math.toRadians(angle), player.getBounds2D().getCenterX(), player.getBounds2D().getCenterY());
g2d.setTransform(at);
g2d.setColor(Color.RED);
g2d.fill(player);
g2d.setColor(Color.BLACK);
g2d.draw(player);
g2d.dispose();
这基本上是将对象的位置转换为玩家的位置,然后围绕对象的中心旋转对象
您还可以应用一个转换,创建该上下文的副本并应用另一个转换,这将变得复合(因此您可以 translate
一个上下文,复制它,然后 rotate
副本和第一个翻译仍适用于副本)
这个令人难以置信的简单示例演示了两个基本示例...
- 使用
Graphics
上下文和AffineTransform
平移和旋转玩家对象(围绕其中心点) - 使用
Path2D
生成变换后的形状(此示例生成两个对象,但您可以使用单个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.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Shape player;
private Point playerPoint;
private float angle;
private float deltaZ = 1.0f;
private int deltaX, deltaY;
public TestPane() {
player = new Rectangle(0, 0, 20, 20);
playerPoint = new Point(80, 80);
Random rnd = new Random();
deltaX = 1;
deltaY = -1;
Timer timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
playerPoint.x += deltaX;
playerPoint.y += deltaY;
Shape rotatedPlayer = rotatedAndTranslatedPlayer();
Rectangle2D bounds = rotatedPlayer.getBounds2D();
if (bounds.getX() < 0.0) {
playerPoint.x = (int)(bounds.getX() * -1);
deltaX *= -1;
} else if (bounds.getX() + bounds.getWidth() >= getWidth()) {
playerPoint.x = getWidth() - (int)bounds.getWidth();
deltaX *= -1;
}
if (bounds.getY() < 0) {
playerPoint.y = 0;
deltaY *= -1;
} else if (bounds.getY() + bounds.getHeight() > getHeight()) {
playerPoint.y = getHeight() - (int)bounds.getHeight();
deltaY *= -1;
}
angle += deltaZ;
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected Shape rotatedAndTranslatedPlayer() {
Path2D.Double rotated = new Path2D.Double(player, AffineTransform.getRotateInstance(
Math.toRadians(angle),
player.getBounds2D().getCenterX(),
player.getBounds2D().getCenterY()));
return new Path2D.Double(rotated, AffineTransform.getTranslateInstance(playerPoint.x, playerPoint.y));
}
// Simply paints the "area" that the player takes up when it's rotated and
// translated
protected void paintAutoTranslatedShape(Graphics2D g2d) {
g2d.setColor(Color.DARK_GRAY);
g2d.fill(rotatedAndTranslatedPlayer().getBounds2D());
}
// Uses a AffineTransform to translate and rotate the player
protected void paintPlayer(Graphics2D g2d) {
AffineTransform at = AffineTransform.getTranslateInstance(playerPoint.x, playerPoint.y);
at.rotate(Math.toRadians(angle), player.getBounds2D().getCenterX(), player.getBounds2D().getCenterY());
g2d.setTransform(at);
g2d.setColor(Color.RED);
g2d.fill(player);
g2d.setColor(Color.BLACK);
g2d.draw(player);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
paintAutoTranslatedShape(g2d);
g2d.dispose();
g2d = (Graphics2D) g.create();
paintPlayer(g2d);
g2d.dispose();
}
}
}