简体   繁体   English

围绕其中心旋转运动形状

[英]Rotate a moving shape around its center

I'm making a 2D game in Java where the player guides a polygon through obstacles. 我正在用Java制作2D游戏,玩家可以通过障碍物引导多边形。 The polygon moves up and down and the game world scrolls left and right. 多边形上下移动,游戏世界向左和向右滚动。 I need the polygon to rotate around its center, but since it is constantly being translated the point it is being rotated around moves. 我需要多边形绕其中心旋转,但是由于它不断地被平移,所以它绕着点旋转。 Trying to translate it back to the original center, rotate it, and translate it back doesn't work. 尝试将其平移回原始中心,然后旋转,然后再平移回原处。 How do I get the center of the shape? 如何获得形状的中心?

Here are my motion calculations on a 2ms timer: 这是我在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();
}
}

And here is my paintComponent method: 这是我的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);
    }
}

where (xPos, yPos) are the center of the screen. 其中(xPos,yPos)是屏幕的中心。

Transformations are (normally) compounding 转换(通常)是复杂的

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

In your above code, you are translating both the original Graphics context and the copy . 在上面的代码中,您正在翻译原始Graphics上下文和copy In this context, copy won't be affected by the original and the original won't be affected by the copy , BUT, the original context is a shared resource and since you don't reset the translation, you will continue to get a translated context each time (compounding). 在这种情况下, copy不会受到原始内容的影响,而原始内容也不会受到copy (BUT)的影响,原始内容是共享资源,并且由于您无需重置翻译,因此您将继续获得每次翻译上下文(复合)。

As a general rule of thumb, do ALL transformations on a copy and dispose of it when you're done. 根据一般经验,对副本进行所有转换,并在完成后将其处理。

For example... 例如...

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

This basically translates the position of the object to the player's position and then rotates the object about the centre of the object 这基本上将对象的位置转换为玩家的位置,然后围绕对象的中心旋转对象

You could, also, apply one transformation, create a copy of that context and apply another transformation, which would become compounded (so you could translate one context, copy it, and then rotate the copy and the first translation would still applied to the copy) 您还可以应用一个转换,创建该上下文的副本,然后应用另一个转换,该转换将变得复杂(因此您可以translate一个上下文,将其复制,然后rotate该副本,并且第一个翻译仍将应用于该副本)

This incredible simple example demonstrates two basic example... 这个难以置信的简单示例演示了两个基本示例...

  1. Using the Graphics context and a AffineTransform to translate and rotate the player object (about it's centre point) 使用Graphics上下文和AffineTransform转换和旋转播放器对象(围绕它的中心点)
  2. Using a Path2D to generate a transformed shape (this example makes two objects, but you could use a single AffineTransform translated and rotated and apply it once). 使用Path2D生成变形的形状(此示例制作了两个对象,但是您可以使用单个AffineTransform平移和旋转并将其应用一次)。

In both cases, they don't affect the original shape 在两种情况下,它们都不会影响原始形状

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

    }
}

In openGL we save transformation state using pushMatrix() and popMatrix() . 在openGL中,我们使用pushMatrix()popMatrix()保存转换状态。 Here their equivalents are Graphics.create() and Graphics.dispose() 在这里,它们的等效项是Graphics.create()Graphics.dispose()

Graphics g1 = g.create();
//do transformations
g1.dispose();

Graphics g2 = g.create();
//do other stuff
g2.dispose();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM