gpt4 book ai didi

java - 如何从 AffineTransform 派生的形状对象中获取 'Get' 特定点

转载 作者:行者123 更新时间:2023-11-30 02:30:38 25 4
gpt4 key购买 nike

作为一个 self 项目,我正在尝试制作游戏“Asteroids”。

目前,我一直在尝试弄清楚如何制作它,以便从我的飞船发射的激光出现在飞船的尖端。到目前为止,我已经尝试使用 Shape 对象的 .getBounds2D().getX() 方法进行实验,但是因为 getBounds2D() 绘制围绕多边形的矩形,激光最终会从我的多边形周围的假想“盒子”的角落出现。

Here's a gif of what I have so far.

有没有办法从 Shape 对象中“获取”特定点?在这种情况下,该特定点是船的尖端。

主类:

public class AsteroidGame implements ActionListener, KeyListener{

public static AsteroidGame game;
public Renderer renderer;

public boolean keyDown = false;
public int playerAngle = 0;

public boolean left = false;
public boolean right = false;
public boolean go = false;
public boolean back = false;
public boolean still = true;
public double angle = 0;
public int turnRight = 5;
public int turnLeft = -5;

public Shape transformed;

public ArrayList<Laser> lasers;
public ArrayList<Shape> transformedLasers;

public final int WIDTH = 1400;
public final int HEIGHT = 800;

public Ship ship;
public Rectangle shipHead;
public Shape shipHeadTrans;
public Point headPoint;


public AffineTransform transform = new AffineTransform();
public AffineTransform lasTransform = new AffineTransform();
public AffineTransform headTransform = new AffineTransform();

public AsteroidGame(){
JFrame jframe = new JFrame();
Timer timer = new Timer(20, this);
renderer = new Renderer();

jframe.add(renderer);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setSize(WIDTH, HEIGHT);
jframe.setVisible(true);
jframe.addKeyListener(this);
jframe.setResizable(false);

int xPoints[] = {800, 780, 800, 820};
int yPoints[] = {400, 460, 440, 460};

//(800, 400) is the initial location of the 'tip' of the ship'.
headPoint = new Point(800, 400);

lasers = new ArrayList<Laser>();
transformedLasers = new ArrayList<Shape>();

ship = new Ship(xPoints, yPoints, 4, 0);
transformed = transform.createTransformedShape(ship);

shipHead = new Rectangle(headPoint);
shipHeadTrans = transform.createTransformedShape(shipHead);
//shipHeadTrans.getBounds2D().

timer.start();

}

public void repaint(Graphics g){

g.setColor(Color.BLACK);
g.fillRect(0, 0, WIDTH, HEIGHT);

Graphics2D g2d = (Graphics2D)g;

//drawing the ship
g2d.setColor(Color.WHITE);
g2d.draw(transformed);

//drawing lasers
g2d.setColor(Color.RED);
for (int i = 0; i < transformedLasers.size(); i++){
System.out.println(i);
g2d.draw(transformedLasers.get(i));
}



}



public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub

/*The for if and else if statements are just to send the ship
* to the other side of the canvas if it ever leaves the screen
*/
if (transformed.getBounds2D().getMinX() > WIDTH){
double tempAng = ship.getAng();
double diff = 90-tempAng;

transform.rotate(Math.toRadians(diff), ship.getCenterX(), ship.getCenterY());
transform.translate(0,WIDTH);
transform.rotate(Math.toRadians(-diff), ship.getCenterX(), ship.getCenterY());

}

else if (transformed.getBounds2D().getX() < 0){
double tempAng = ship.getAng();
double diff = 90-tempAng;
transform.rotate(Math.toRadians(diff), ship.getCenterX(), ship.getCenterY());
transform.translate(0,-WIDTH);
transform.rotate(Math.toRadians(-diff), ship.getCenterX(), ship.getCenterY());
}

else if (transformed.getBounds2D().getY() > HEIGHT){
double tempAng = ship.getAng();
double diff = 180-tempAng;
transform.rotate(Math.toRadians(diff), ship.getCenterX(), ship.getCenterY());
transform.translate(0,HEIGHT);
transform.rotate(Math.toRadians(-diff), ship.getCenterX(), ship.getCenterY());
}

else if (transformed.getBounds2D().getY() < 0){
double tempAng = ship.getAng();
double diff = 180-tempAng;
transform.rotate(Math.toRadians(diff), ship.getCenterX(), ship.getCenterY());
transform.translate(0,-HEIGHT);
transform.rotate(Math.toRadians(-diff), ship.getCenterX(), ship.getCenterY());
}


if (right){
ship.right();
//rotating the ship
transform.rotate(Math.toRadians(turnRight), ship.getCenterX(), ship.getCenterY());
//rotating the 'tip' of the ship.
headTransform.rotate(Math.toRadians(turnRight), ship.getCenterX(), ship.getCenterY());
}

else if (left){
ship.left();
//rotating the ship
transform.rotate(Math.toRadians(turnLeft), ship.getCenterX(), ship.getCenterY());
//rotating the 'tip' of the ship
headTransform.rotate(Math.toRadians(turnLeft), ship.getCenterX(), ship.getCenterY());
}
if (go){
ship.go();
}
else if (back){
ship.reverse();
}

//moving and shaping each individual laser that had been shot
for (int i = 0; i < transformedLasers.size(); i++){
lasers.get(i).move();

lasTransform = new AffineTransform();
lasTransform.rotate(Math.toRadians(lasers.get(i).getAng()), transformed.getBounds2D().getX(), transformed.getBounds2D().getY());
transformedLasers.set(i, lasTransform.createTransformedShape(lasers.get(i)));

}

//moving the ship
ship.move();

//moving the 'tip'
shipHead.y -= ship.getSpeed();

transformed = transform.createTransformedShape(ship);
shipHeadTrans = headTransform.createTransformedShape(shipHead);


renderer.repaint();

}

//defining a new laser
public void fireLaser(){
Laser tempLaser = new Laser((int)transformed.getBounds2D().getX(), (int)transformed.getBounds2D().getY(), 5, 10, ship.getAng());
lasers.add(tempLaser);

lasTransform = new AffineTransform();
lasTransform.rotate(Math.toRadians(ship.getAng()), transformed.getBounds2D().getX(), transformed.getBounds2D().getY());
transformedLasers.add(lasTransform.createTransformedShape(tempLaser));

}

public static void main(String[] args){
game = new AsteroidGame();
}

@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
right = true;
keyDown = true;
}else if (e.getKeyCode() == KeyEvent.VK_LEFT){
left = true;
keyDown = true;
}

else if (e.getKeyCode() == KeyEvent.VK_UP){
go = true;
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN){
back = true;
}

//fire laser
if (e.getKeyCode() == KeyEvent.VK_SPACE){
fireLaser();
}

}

@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
right = false;
}
if (e.getKeyCode() == KeyEvent.VK_LEFT){
left = false;
}
if (e.getKeyCode() == KeyEvent.VK_UP){
go = false;
}
if (e.getKeyCode() == KeyEvent.VK_DOWN){
back = false;
}
still = true;
keyDown = false;
}

@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub

}

船舶类别(但我认为这不相关)

package asteroidGame;

import java.awt.Polygon;
import java.util.Arrays;

public class Ship extends Polygon{

/**
*
*/
private double currSpeed = 0;

private static final long serialVersionUID = 1L;
public double angle;
public int[] midX;
public int[] midY;

public Ship(int[] x, int[] y, int points, double angle){
super(x, y, points);
midX = x;
midY = y;

this.angle= angle;
}


public void right(){
angle += 5;
}
public void left(){
angle -= 5;
}

public void move(){
for (int i = 0; i < super.ypoints.length; i++){
super.ypoints[i] -= currSpeed;
//System.out.println(super.ypoints[i]);
//System.out.println(super.xpoints[i]);
}
//System.out.println(Arrays.toString(super.ypoints));



}
public double getSpeed(){
return currSpeed;
}


public void reverse(){
if (currSpeed > -15) currSpeed -= 0.2;
}

public void go(){
if (currSpeed < 25) currSpeed += 0.5;

}

public int getCenterX(){
return super.xpoints[2];
}
public int getCenterY(){
return super.ypoints[2];
}

public double getAng(){
return angle;
}
public void test(){
for (int x = 0; x < super.ypoints.length; x++){
super.ypoints[x] += 1000;
}
}

/*
public void decrement(){
if(currSpeed == 0){}

else if (currSpeed > 0 && currSpeed < 15){
currSpeed -= 0.05;

}
else if (currSpeed < 0 && currSpeed > -15){
currSpeed += 0.05;

}
System.out.println("losing speed");
}
*/
}

激光类(class)(我认为这也不相关,但你开始吧。)

package asteroidGame;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;

public class Laser extends Rectangle{

private double angle;

public Laser(int x, int y , int width, int height, double ang){
super(x, y, width, height);
angle = ang;
Rectangle tst = new Rectangle();


}

public void move(){
super.y -= 35;
}

public double getAng(){
return angle;
}

public boolean intersects (Rectangle2D r){
//if intersects
if (super.intersects(r)){
return true;
}
else{
return false;
}

}


}

我正在考虑将 Shape 对象转换 重新转换为 Polygon 来获取要点,但我不确定这如何或是否可行。

最佳答案

您可以使用 AffineTransform.transform(Point2D, Point2D) 变换多边形上的单个点。

如果你不尝试使用旋转变换来移动飞船,而是保留一个 (x,y) ,事情会简单得多。船舶所在位置。您可以将船舶的位置移动到 move()而不是尝试平移多边形。然后,当你想给船绘制(paint)时,你可以做:

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

要根据速度移动点,您可以执行以下操作来查找移动 vector :

double velX = speed * Math.cos(angle);
double velY = speed * Math.sin(angle);
locX += timeElapsed * velX;
locY += timeElapsed * velY;

这本质上是从极坐标到笛卡尔坐标的转换。 x 和 y 速度是斜边为 speed 的三角形的边。其已知角度为angle :

             /|
/ |
/ |
/ |
speed / |
/ |
/ |velY
/ angle |
/)_______|
velX

我的回答中有一个以这种方式进行移动的示例:https://stackoverflow.com/a/43692434/2891664 .

<小时/>

您的意见:

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?

或多或少,是的。您仍然有一个多边形来保持船舶的形状,但多边形上的点将相对于 (0,0) .

假设以下定义:

  • 每个(x,y)多边形上的点是 p<sub>i</sub> 。 (换句话说, p<sub>0</sub>p<sub>1</sub>p<sub>2</sub>p<sub>3</sub> 之一。)
  • (x,y)翻译的坐标是 T

然后,翻译完 Graphics2D ,每个p<sub>i</sub>坐标变为p<sub>i</sub>+T在面板上。因此,如果您的多边形点是相对于 (0,0) 定义的然后翻译成船的(locX,locY)会将多边形移动到相对于 (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};

例如在同一个地方启动飞船,您可以将其位置初始化为 (800,400) .

<小时/>

我再次思考这个问题,并意识到旋转有点复杂,因为你可能不想让船绕着尖端旋转。您可能想绕其中心旋转船。

因此,这是一个 MCVE,演示了如何完成所有这一切。

movement example

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

还有其他方法可以计算船的尖端,但我在 MCVE 中的方法是这样的:

  1. 获取船舶的中心点,相对于 (0,0) .
  2. 获取从中心点到尖端的距离。小费在(0,0)所以这只是中心的 y 坐标。
  3. 然后计算(x,y)尖端相对于中心的位置。这与上图的速度和速率非常相似,但斜边是船中心和尖端之间的距离。
  4. 将中心转换为相对于船舶位置的中心。
  5. 将尖端的位置(相对于中心)转换为相对于船舶的位置。

这也可以通过AffineTransform来完成。 ,类似于您在问题的代码中所做的事情,但您会在每次更新时设置它。像这样的事情:

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

您仍然可以使用变换来以这种方式进行计算,但是您不会因为依赖变换来进行运动而感到任何奇怪。 (在问题的代码中,例如,船仅沿着 y 轴移动。明显的侧向移动是由于一系列旋转串联造成的。)

关于java - 如何从 AffineTransform 派生的形状对象中获取 'Get' 特定点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44371135/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com