如何在Java中独立旋转对象/图像?

时间:2015-08-12 07:40:35

标签: java animation rotation transform

我试图让一系列矩形围绕它们各自的中心旋转并向右移动,但由于该平面包含所有矩形,所以所有矩形一致地围绕最新添加的矩形的中心旋转而不是独立地围绕他们各自的中心。以下是代码:

主程序:

import java.awt.Graphics2D;
import java.util.ArrayList;

public class Rectangles {

    public static final int SCREEN_WIDTH = 400;
    public static final int SCREEN_HEIGHT = 400;

    public static void main(String[] args) throws Exception {
        Rectangles game = new Rectangles();
        game.play();
    }

    public void play() {
        board.setupAndDisplay();
    }

    public Rectangles() {
        board = new Board(SCREEN_WIDTH, SCREEN_HEIGHT, this);
        rectangle_2 = new Rectangle_2();
        rectangles = new ArrayList<Rectangle_2>();
    }

    public void drawRectangles(Graphics2D g, float elapsedTime) {
        ticks++;
        if (ticks % 4000 == 0) {
            Rectangle_2 rectangle = new Rectangle_2();
            rectangles.add(rectangle);
        }
        rotateRectangles(g);
        drawRectangles(g);
        moveRectangles(elapsedTime);

        for (int i = 0; i < rectangles.size(); i++) {
            Rectangle_2 rectangle = rectangles.get(i);
            if (rectangle.getX() < -75) {
                rectangles.remove(i);
                i--;
            }
        }
    }

    public void drawRectangles(Graphics2D g) {
        for (Rectangle_2 rectangle: rectangles) {
            rectangle.drawRectangle(g);
        }
    }

    public void rotateRectangles(Graphics2D g) {
        for (Rectangle_2 rectangle: rectangles) {
            rectangle.rotateRectangle(g);
        }
    }

    public void moveRectangles(float elapsedTime) {
        for (Rectangle_2 rectangle: rectangles) {
            rectangle.move(10 * elapsedTime);
        }
    }

    private Board board;
    private Rectangle_2 rectangle_2;
    private int ticks = 0;
    private ArrayList<Rectangle_2> rectangles;
}

矩形类:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;

public class Rectangle_2 {

    public Rectangle_2() {
        x = 0;
        y = 200;
        rectangle = new Rectangle((int) x, (int) y, 25, 25);
    }

    public void drawRectangle(Graphics2D g) {
        g.setColor(Color.red);
        g.draw(rectangle);
    }

    public void rotateRectangle(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        angle += 0.001;
        g2.rotate(angle, rectangle.getX() + rectangle.getWidth() / 2, rectangle.getY() + rectangle.getHeight() / 2);
        g2.setColor(Color.red);
    }

    public void move(float elapsedTime) {
        x = x + elapsedTime;
        rectangle.setLocation((int) x, (int) y);
    }

    public boolean collides(Rectangle r) {
        return r.intersects(rectangle);
    }

    @Override
    public String toString() {
        return "Pipe [x = " + x + ", y = " + y + ", rectangle = " + rectangle + "]";
    }

    public Rectangle getRectangle() {
        return rectangle;
    }

    public double getX() {
        return x;
    }

    private double x;
    private double y;
    private double angle = 0;
    private Rectangle rectangle;
}

动画发生的棋盘类:

import java.awt.*;
import javax.swing.*;

public class Board extends JPanel {

    private static final long serialVersionUID = 1L;

    public Board(int width_, int height_, Rectangles simulator_) {
        width = width_;
        height = height_;
        game = simulator_;
        lastTime = -1L;
    }

    public void setupAndDisplay() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new JScrollPane(this));
        f.setSize(width, height);
        f.setLocation(200, 200);
        f.setVisible(true);
        this.setFocusable(true);
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        boolean first = (lastTime == -1L);
        long elapsedTime = System.nanoTime() - lastTime;
        lastTime = System.nanoTime();
        g.setColor(Color.white);
        g.fillRect(0, 0, width, height);
        g.setColor(Color.white);
        game.drawRectangles((Graphics2D) g, (first ? 0.0f : (float) elapsedTime / 1e9f));
        repaint();
    }

    private int width;
    private int height;
    private long lastTime;
    private Rectangles game;
}

请注意,由于实施了延迟,矩形需要几秒钟才能显示。谢谢 :)。

1 个答案:

答案 0 :(得分:0)

Graphics上下文是一个共享资源,也就是说,在单个绘制周期中,所有组件都获得相同的Graphics上下文。您对Graphics上下文所做的任何更改也会得到维护(或者在相同转换的情况下为复合)。所以这意味着,每当你调用Graphics#rotate时,你实际上正在复合可能已在其上执行的任何先前轮换。

您需要以两种方式更改代码......

  1. 您需要独立的更新周期,与油漆周期无关
  2. 在应用任何转换之前创建Graphics上下文的本地副本
  3. 例如......

    enter image description here

    Rectangles成为主要的驱动程序/引擎。它负责管理实体并在每个周期更新它们。通常情况下,我会使用某种interface来描述其他案例可能使用的功能,但是你明白了这个想法

    public class Rectangles {
    
        public static void main(String[] args) throws Exception {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
                    Rectangles game = new Rectangles();
                    game.play();
                }
            });
        }
    
        public void play() {
    
            Board board = new Board(this);
    
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(board);
            f.pack();
            f.setLocation(200, 200);
            f.setVisible(true);
            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    updateRectangles();
                    board.repaint();
                    lastTime = System.nanoTime();
                }
            });
            timer.start();
        }
    
        public Rectangles() {
            rectangle_2 = new Rectangle_2();
            rectangles = new ArrayList<Rectangle_2>();
        }
    
        protected void updateRectangles() {
            boolean first = (lastTime == -1L);
            double elapsedTime = System.nanoTime() - lastTime;
            elapsedTime = (first ? 0.0f : (float) elapsedTime / 1e9f);
    
            ticks++;
            if (ticks <= 1 || ticks % 100 == 0) {
                Rectangle_2 rectangle = new Rectangle_2();
                rectangles.add(rectangle);
            }
    
            rotateRectangles();
            moveRectangles(elapsedTime);
    
    
            for (int i = 0; i < rectangles.size(); i++) {
                Rectangle_2 rectangle = rectangles.get(i);
                if (rectangle.getX() < -75) {
                    rectangles.remove(i);
                    i--;
                }
            }
        }
    
        public void drawRectangles(Graphics2D g) {
            for (Rectangle_2 rectangle : rectangles) {
                rectangle.drawRectangle(g);
            }
        }
    
        protected void rotateRectangles() {
            for (Rectangle_2 rectangle : rectangles) {
                rectangle.rotateRectangle();
            }
        }
    
        protected void moveRectangles(double elapsedTime) {
            for (Rectangle_2 rectangle : rectangles) {
                rectangle.move(10 * elapsedTime);
            }
        }
    
        private long lastTime = -1L;
        private Rectangle_2 rectangle_2;
        private int ticks = 0;
        private ArrayList<Rectangle_2> rectangles;
    }
    

    Board只不过是可以渲染实体的表面

    public class Board extends JPanel {
    
        public Board(Rectangles engine) {
            game = engine;
        }
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            game.drawRectangles((Graphics2D) g);
        }
    
        private Rectangles game;
    }
    

    Rectangle_2是一个简单的信息容器,它知道如何在有机会时自己画画。你会注意到移动和旋转方法只是更新状态,它们什么都不做。

    drawRectangle首先创建提供的Graphics2D上下文的副本,然后再应用它的更改并呈现矩形,完成后调用dispose来处理副本

    public class Rectangle_2 {
    
        public Rectangle_2() {
            x = 0;
            y = 200;
            rectangle = new Rectangle((int) x, (int) y, 25, 25);
        }
    
        public void drawRectangle(Graphics2D g) {
            Graphics2D g2 = (Graphics2D) g.create();
            g2.rotate(angle, rectangle.getX() + rectangle.getWidth() / 2, rectangle.getY() + rectangle.getHeight() / 2);
            g2.setColor(Color.red);
            g2.draw(rectangle);
            g2.dispose();
        }
    
        public void rotateRectangle() {
            angle += 0.001;
        }
    
        public void move(double elapsedTime) {
            x = x + elapsedTime;
            rectangle.setLocation((int) x, (int) y);
        }
    
        public boolean collides(Rectangle r) {
            return r.intersects(rectangle);
        }
    
        @Override
        public String toString() {
            return "Pipe [x = " + x + ", y = " + y + ", rectangle = " + rectangle + "]";
        }
    
        public Rectangle getRectangle() {
            return rectangle;
        }
    
        public double getX() {
            return x;
        }
    
        private double x;
        private double y;
        private double angle = 0;
        private Rectangle rectangle;
    }