Java 围绕其中心旋转移动的形状

Java 围绕其中心旋转移动的形状,java,rotation,graphics2d,affinetransform,Java,Rotation,Graphics2d,Affinetransform,我正在用Java制作一个2D游戏,玩家引导一个多边形穿过障碍物。多边形上下移动,游戏世界左右滚动。我需要多边形围绕其中心旋转,但由于它不断地被平移,所以它正在围绕移动旋转。尝试将其平移回原始中心,旋转它,然后将其平移回无法工作。如何获得形状的中心 以下是我在2ms定时器上的运动计算: @Override public void actionPerformed(ActionEvent e) { double theta = angleRad+90; if (up == true)

我正在用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);
    }
}

其中(XPO、YPO)是屏幕的中心。

在openGL中,我们使用
pushMatrix()
popMatrix()
保存转换状态。这里它们的等价物是
Graphics.create()
Graphics.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);
    }
}
在上面的代码中,您正在翻译原始的
图形
上下文和
副本
。在此上下文中,
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();
这基本上是将对象的位置转换为玩家的位置,然后围绕对象的中心旋转对象

您还可以应用一个转换,创建该上下文的副本,然后应用另一个转换,该转换将变得复杂(因此您可以
翻译
一个上下文,复制它,然后
旋转
副本,并且第一个翻译仍将应用于副本)

这个令人难以置信的简单示例演示了两个基本示例

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();
  • 使用
    图形
    上下文和
    仿射变换
    平移和旋转播放器对象(围绕其中心点)
  • 使用
    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.awt.Color;
    导入java.awt.Dimension;
    导入java.awt.EventQueue;
    导入java.awt.Graphics;
    导入java.awt.Graphics2D;
    导入java.awt.Point;
    导入java.awt.Rectangle;
    导入java.awt.Shape;
    导入java.awt.event.ActionEvent;
    导入java.awt.event.ActionListener;
    导入java.awt.geom.AffineTransform;
    导入java.awt.geom.Path2D;
    导入java.awt.geom.Rectangle2D;
    导入java.util.Random;
    导入javax.swing.JFrame;
    导入javax.swing.JPanel;
    导入javax.swing.Timer;
    公开课考试{
    公共静态void main(字符串[]args){
    新测试();
    }
    公开考试(){
    invokeLater(新的Runnable(){
    @凌驾
    公开募捐{
    JFrame=新JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(newtestpane());
    frame.pack();
    frame.setLocationRelativeTo(空);
    frame.setVisible(true);
    }
    });
    }
    公共类TestPane扩展了JPanel{
    私人造型球员;
    私人点播放点;
    私人浮动角度;
    私人浮动deltaZ=1.0f;
    德尔泰私人酒店;
    公共测试窗格(){
    player=新矩形(0,0,20,20);
    playerPoint=新点(80,80);
    随机rnd=新随机();
    deltaX=1;
    deltaY=-1;
    计时器计时器=新计时器(5,新ActionListener(){
    @凌驾
    已执行的公共无效操作(操作事件e){
    playerPoint.x+=deltaX;
    playerPoint.y+=三角洲;
    形状rotatedPlayer=rotatedAndTranslatedPlayer();
    矩形2D边界=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){
    播放点y=0;
    三角洲*=-1;
    }else if(bounds.getY()+bounds.getHeight()>getHeight()){
    playerPoint.y=getHeight()-(int)bounds.getHeight();
    三角洲*=-1;
    }
    角度+=三角洲;
    重新油漆();
    }
    });
    timer.start();
    }
    @凌驾
    公共维度getPreferredSize(){
    返回新维度(200200);
    }
    受保护的形状rotatedAndTranslatedPlayer(){
    Path2D.Double rotated=新的Path2D.Double(播放器,仿射Transform.getRotateInstance(
    数学,托拉迪安(角),
    player.getBounds2D().getCenterX(),
    player.getBounds2D().getCenterY());
    返回新的Path2D.Double(旋转,仿射Transform.getTranslateInstance(playerPoint.x,playerPoint.y));
    }
    //简单地画出玩家旋转和移动时占据的“区域”
    //翻译
    受保护的空心油漆自动转换形状(图形2D-g2d){
    g2d.setColor(颜色:深灰色);
    填充(rotatedAndTranslatedPlayer().getBounds2D());
    }
    //使用仿射变换来平移和旋转播放器
    受保护的void paintPlayer(Graphics2D g2d){