java衍生作用_java-如何从AffineTransform衍生的形状对象中“...
您可以使用AffineTransform.transform(Point2D, Point2D)變換多邊形上的單個(gè)點(diǎn).
如果您不使用旋轉(zhuǎn)變換來移動(dòng)船,而是將船的位置保持在一個(gè)(x,y)位置,那么事情就簡單得多.您可以在move()中移動(dòng)飛船的位置,而不是嘗試平移多邊形.然后,當(dāng)您想給船上油漆時(shí),例如做:
// Optionally copying the Graphics so the
// transform doesn't affect later painting.
Graphics2D temp = (Graphics2D) g2d.create();
temp.translate(ship.locX, ship.locY);
temp.rotate(ship.angle);
temp.draw(ship);
要基于速度移動(dòng)點(diǎn),可以執(zhí)行以下操作來找到運(yùn)動(dòng)向量:
double velX = speed * Math.cos(angle);
double velY = speed * Math.sin(angle);
locX += timeElapsed * velX;
locY += timeElapsed * velY;
這實(shí)質(zhì)上是從極坐標(biāo)到笛卡爾坐標(biāo)的轉(zhuǎn)換. x和y速度是三角形的邊,其斜邊為速度,已知角度為角度:
/|
/ |
/ |
/ |
speed / |
/ |
/ |velY
/ angle |
/)_______|
velX
對于您的評論:
Are you saying that, unlike my initial move function, just to make ship hold a single point, and thus I would only translate that instead?
或多或少,是的.您仍然需要有一個(gè)多邊形來保持船的形狀,但是多邊形上的點(diǎn)將相對于(0,0).
假設(shè)以下定義:
>多邊形上的每個(gè)(x,y)點(diǎn)都是pi. (換句話說,p0,p1,p2和p3中的一個(gè).)
>平移的(x,y)坐標(biāo)為T
然后,在轉(zhuǎn)換Graphics2D之后,每個(gè)pi坐標(biāo)在面板上變?yōu)閜iT.因此,如果您的多邊形點(diǎn)是相對于(0,0)定義的,則平移到飛船的(locX,locY)會(huì)將多邊形移動(dòng)到相對于(locX,locY)的位置.
然后,將多邊形的尖端定義為(0,0)可能是最簡單的,這樣在平移之后,船的尖端就是船的位置:
// Your original points:
int xPoints[] = {800, 780, 800, 820};
int yPoints[] = {400, 460, 440, 460};
// Become these points relative to (0,0):
int xPoints[] = {0, -20, 0, 20};
int yPoints[] = {0, 60, 40, 60};
和例如在同一個(gè)地方開船,您將其位置初始化為(800,400).
我又想了一下,意識到旋轉(zhuǎn)有點(diǎn)復(fù)雜,因?yàn)槟赡懿幌肜@著尖端旋轉(zhuǎn)船.您可能想繞船的中心旋轉(zhuǎn)船.
因此,這里有一個(gè)MCVE演示了如何進(jìn)行所有這些操作.
package mcve.game;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.GraphicsConfiguration;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
public class MovementExample implements ActionListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(MovementExample::new);
}
final int fps = 60;
final int period = 1000 / fps;
final JFrame frame;
final GamePanel panel;
final Controls controls;
final Ship ship;
final List bullets = new ArrayList<>();
MovementExample() {
frame = new JFrame("Movement Example");
Dimension size = getMaximumWindowSize(frame);
size.width /= 2;
size.height /= 2;
frame.setPreferredSize(size);
panel = new GamePanel();
frame.setContentPane(panel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
controls = new Controls();
ship = new Ship(panel.getWidth() / 2,
panel.getHeight() / 2);
new Timer(period, this).start();
}
@Override
public void actionPerformed(ActionEvent e) {
double secondsElapsed = 1.0 / fps;
ship.update(secondsElapsed);
bullets.forEach(b -> b.update(secondsElapsed));
Rectangle bounds = panel.getBounds();
bullets.removeIf(b -> !bounds.contains(b.locX, b.locY));
panel.repaint();
}
class GamePanel extends JPanel {
GamePanel() {
setBackground(Color.WHITE);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (ship != null) {
ship.draw(g2);
}
bullets.forEach(b -> b.draw(g2));
g2.dispose();
}
}
abstract class AbstractGameObject {
double maxSpeed;
double rotationAngle;
double locX;
double locY;
double velX;
double velY;
AbstractGameObject(double initialX, double initialY) {
locX = initialX;
locY = initialY;
}
abstract void update(double secondsElapsed);
abstract void draw(Graphics2D g2);
}
class Ship extends AbstractGameObject {
Polygon shape;
double rotationRate;
Ship(double initialX, double initialY) {
super(initialX, initialY);
maxSpeed = 128; // pixels/second
rotationAngle = Math.PI * 3 / 2;
rotationRate = (2 * Math.PI) / 2; // radians/second
int xPoints[] = {0, -20, 0, 20};
int yPoints[] = {0, 60, 40, 60};
shape = new Polygon(xPoints, yPoints, 4);
}
Point2D.Double getTip() {
Point2D.Double center = getCenter();
// The tip is at (0,0) and it's already centered
// on the x-axis origin, so the distance from the
// tip to the center is just center.y.
double distance = center.y;
// Then find the location of the tip, relative
// to the center.
double tipX = distance * Math.cos(rotationAngle);
double tipY = distance * Math.sin(rotationAngle);
// Now find the actual location of the center.
center.x += locX;
center.y += locY;
// And return the actual location of the tip, relative
// to the actual location of the center.
return new Point2D.Double(tipX + center.x, tipY + center.y);
}
Point2D.Double getCenter() {
// Returns the center point of the ship,
// relative to (0,0).
Point2D.Double center = new Point2D.Double();
for (int i = 0; i < shape.npoints; ++i) {
center.x += shape.xpoints[i];
center.y += shape.ypoints[i];
}
center.x /= shape.npoints;
center.y /= shape.npoints;
return center;
}
@Override
void update(double secondsElapsed) {
// See my answer here: https://stackoverflow.com/a/43692434/2891664
// for a discussion of why this logic is the way it is.
double speed = 0;
if (controls.isUpHeld()) {
speed += maxSpeed;
}
if (controls.isDownHeld()) {
speed -= maxSpeed;
}
velX = speed * Math.cos(rotationAngle);
velY = speed * Math.sin(rotationAngle);
locX += secondsElapsed * velX;
locY += secondsElapsed * velY;
double rotation = 0;
if (controls.isLeftHeld()) {
rotation -= rotationRate;
}
if (controls.isRightHeld()) {
rotation += rotationRate;
}
rotationAngle += secondsElapsed * rotation;
// Cap the angle so it can never e.g. get so
// large that it loses precision.
if (rotationAngle > 2 * Math.PI) {
rotationAngle -= 2 * Math.PI;
}
if (controls.isFireHeld()) {
Point2D.Double tipLoc = getTip();
Bullet bullet = new Bullet(tipLoc.x, tipLoc.y, rotationAngle);
bullets.add(bullet);
}
}
@Override
void draw(Graphics2D g2) {
Graphics2D copy = (Graphics2D) g2.create();
copy.setColor(Color.RED);
// Translate to the ship's location.
copy.translate(locX, locY);
// Rotate the ship around its center.
Point2D.Double center = getCenter();
// The PI/2 offset is necessary because the
// polygon points are defined with the ship
// already vertical, i.e. at an angle of -PI/2.
copy.rotate(rotationAngle + (Math.PI / 2), center.x, center.y);
copy.fill(shape);
}
}
class Bullet extends AbstractGameObject {
Ellipse2D.Double shape = new Ellipse2D.Double();
Bullet(double initialX, double initialY, double initialRotation) {
super(initialX, initialY);
maxSpeed = 512;
rotationAngle = initialRotation;
velX = maxSpeed * Math.cos(rotationAngle);
velY = maxSpeed * Math.sin(rotationAngle);
double radius = 3;
shape.setFrame(-radius, -radius, 2 * radius, 2 * radius);
}
@Override
void update(double secondsElapsed) {
locX += secondsElapsed * velX;
locY += secondsElapsed * velY;
}
@Override
void draw(Graphics2D g2) {
Graphics2D copy = (Graphics2D) g2.create();
copy.setColor(Color.BLACK);
copy.translate(locX, locY);
copy.fill(shape);
}
}
// See https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
class Controls {
final Set keysHeld = new HashSet<>();
Controls() {
bind(KeyEvent.VK_A, "left");
bind(KeyEvent.VK_D, "right");
bind(KeyEvent.VK_W, "up");
bind(KeyEvent.VK_S, "down");
bind(KeyEvent.VK_SPACE, "fire");
}
boolean isLeftHeld() { return keysHeld.contains(KeyEvent.VK_A); }
boolean isRightHeld() { return keysHeld.contains(KeyEvent.VK_D); }
boolean isUpHeld() { return keysHeld.contains(KeyEvent.VK_W); }
boolean isDownHeld() { return keysHeld.contains(KeyEvent.VK_S); }
boolean isFireHeld() { return keysHeld.contains(KeyEvent.VK_SPACE); }
void bind(int keyCode, String name) {
bind(keyCode, name, true);
bind(keyCode, name, false);
}
void bind(int keyCode, String name, boolean isOnRelease) {
KeyStroke stroke = KeyStroke.getKeyStroke(keyCode, 0, isOnRelease);
name += isOnRelease ? ".released" : ".pressed";
panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(stroke, name);
panel.getActionMap()
.put(name, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (isOnRelease) {
keysHeld.remove(keyCode);
} else {
keysHeld.add(keyCode);
}
}
});
}
}
// This returns the usable size of the display which
// the JFrame resides in, as described here:
// http://docs.oracle.com/javase/8/docs/api/java/awt/GraphicsEnvironment.html#getMaximumWindowBounds--
static Dimension getMaximumWindowSize(JFrame frame) {
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Dimension size = config.getBounds().getSize();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
size.width -= insets.left + insets.right;
size.height -= insets.top + insets.bottom;
return size;
}
}
還有其他方法可以計(jì)算出船的頂端,但是我在MCVE中做到的方式是:
>獲取相對于(0,0)的船的中心點(diǎn).
>獲取從中心點(diǎn)到尖端的距離.尖端在(0,0),因此這只是中心的y坐標(biāo).
>然后計(jì)算尖端相對于中心的(x,y)位置.速度和速度與上圖非常相似,但是斜邊是船的中心與船頭之間的距離.
>將中心平移為相對于船的位置.
>將尖端的位置(相對于中心)轉(zhuǎn)換為相對于船的位置.
也可以使用AffineTransform完成所有操作,類似于您在問題代碼中所做的操作,但是您需要在每次更新時(shí)進(jìn)行設(shè)置.像這樣:
AffineTransform transform = new AffineTransform();
@Override
void update(double secondsElapsed) {
...
// Clear the previous translation and rotation.
transform.setToIdentity();
// Set to current.
transform.translate(locX, locY);
Point2D.Double center = getCenter();
transform.rotate(rotationAngle + (Math.PI / 2), center.x, center.y);
if (controls.isFireHeld()) {
Point2D.Double tip = new Point2D.Double(0, 0);
transform.transform(tip, tip);
Bullet bullet = new Bullet(tip.x, tip.y, rotationAngle);
bullets.add(bullet);
}
}
您仍然可以通過這種方式使用變換來進(jìn)行計(jì)算,但最終不會(huì)因依賴于移動(dòng)而產(chǎn)生任何怪異. (在問題代碼中,例如,船舶僅沿y軸移動(dòng).明顯的側(cè)向移動(dòng)是由于一系列旋轉(zhuǎn)串聯(lián)所致.)
總結(jié)
以上是生活随笔為你收集整理的java衍生作用_java-如何从AffineTransform衍生的形状对象中“...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 网易云音乐获取音频链接(爬虫)破解par
- 下一篇: 统计nginx日志里访问次数最多的前十个
