如何使用内置 java 图形制作能够旋转的自上而下视图?

How do I make a top down view with the ability to rotate with built in java graphics?

我正在尝试在屏幕中间的静态玩家上制作一个自上而下视图的赛车游戏,因此地图不会在地图上移动玩家,而是会在玩家周围移动。由于这是一款赛车游戏,我希望它也有点类似于汽车,但我一直无法围绕玩家旋转地图并进行翻译。

我尝试通过添加或减去中心来跟踪中心,这是我为翻译所做的,但它不适用于旋转方法。 rotate 函数不会围绕玩家旋转,而是会围绕其他某个点旋转玩家,平移会捕捉到与旋转不同的位置。我确定我的方法有缺陷,并且我已经阅读了有关层等的信息,但是我不确定我可以用它们做什么或如何使用它们。此外,如有任何关于如何使用 java 图形的建议,我们将不胜感激!

这是我的主要内容:

import javax.swing.JFrame;

import java.awt.BorderLayout;

public class game 
{
    public static void main(String []args)
    {
        JFrame frame = new JFrame();
        final int FRAME_WIDTH = 1000;
        final int FRAME_HEIGHT = 600;
        frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final Map b = new Map();
        frame.add(b,BorderLayout.CENTER);
        frame.setVisible(true); 
        b.startAnimation();
    }
}

这是处理所有图形的class

import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JComponent;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Map extends JComponent implements Runnable, KeyListener
{   
    private int speed = 5;
    private int xcenter = 500; // starts on player
    private int ycenter = 300;
    private double angle = 0.0;
    
    private int[] xcords = {xcenter+10, xcenter, xcenter+20};
    private int[] ycords = {ycenter-10, ycenter+20, ycenter+20};
    
    private boolean moveNorth = false;
    private boolean moveEast = false;
    private boolean moveSouth = false;
    private boolean moveWest = false;

    public Map()
    {
        addKeyListener(this);
        setFocusable(true);
        setFocusTraversalKeysEnabled(false);
    }
    
    public void startAnimation()
    {
        Thread t = new Thread(this);
        t.start();
    }
    
    public void paintComponent(Graphics g)
    {
        g.fillPolygon(xcords, ycords, 3);

        // move screen
        if(moveNorth)
        {
            ycenter += speed;
            g.translate(xcenter, ycenter);
        }
        
        else if(moveEast)
        {
            angle += ((1 * Math.PI/180) % (2 * Math.PI));
            ((Graphics2D) g).rotate(angle, 0, 0);
        }
        
        else if(moveSouth)
        {
            System.out.println(xcenter + ", " + ycenter);
            ycenter -= speed;
            ((Graphics2D) g).rotate(angle, 0, 0);
            g.translate(xcenter, ycenter);
        }
        
        else if(moveWest)
        {
            angle -= Math.toRadians(1) % (2 * Math.PI);
            ((Graphics2D) g).rotate(angle, 0, 0);
        }
        for(int i = -10; i < 21; i++)
        {
            g.drawLine(i * 50, -1000, i * 50, 1000);
            g.drawLine(-1000, i * 50, 1000, i * 50);
        }
        g.drawOval(0, 0, 35, 35);

    }
    
    public void run()
    {
        while (true)
        {
            try
            {
                if(moveNorth || moveEast || moveSouth || moveWest)
                {
                    repaint();
                }
                Thread.sleep(10);
            }
            catch (InterruptedException e)
            {
            
            }
        }
    }
    
    public void keyPressed(KeyEvent e)
    {
        if(e.getExtendedKeyCode() == 68) // d
        {
            moveEast = true;
        }
        
        else if(e.getExtendedKeyCode() == 87) // w
        {
            moveNorth = true;
        }
        
        else if(e.getExtendedKeyCode() == 65) // a
        {
            moveWest = true;
        }
        
        else if(e.getExtendedKeyCode() == 83) // s
        {
            moveSouth = true;
        }
        
    }
    
    public void keyReleased(KeyEvent e)
    {
        moveNorth = false;
        moveEast = false;
        moveSouth = false;
        moveWest = false;
    }
    
    public void keyTyped(KeyEvent e)
    {
        
    }
}

你必须记住变换是复合的,所以如果你将 Graphics 上下文旋转 45 度,那么在它之后绘制的所有内容都会旋转 45 度(围绕旋转点),如果你再旋转45度,之后画的东西一共旋转90度。

如果你想在转换后绘制额外的内容,那么你要么需要撤消转换,要么最好拍摄 Graphics 上下文的快照并在你处理时处理它(快照)大功告成。

您还需要注意旋转点,Graphics2D#rotate(double) 会围绕原点(即0x0)旋转Graphics,这可能是不可取的。您可以通过更改原点(即平移)或使用 Graphics2D#rotate(double, double, double) 来更改此设置,后者允许您定义旋转点。

例如...

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }

    public class TestPane extends JPanel {

        enum Direction {
            LEFT, RIGHT;
        }

        protected enum InputAction {
            PRESSED_LEFT, PRESSED_RIGHT, RELEASED_LEFT, RELEASED_RIGHT
        }

        private BufferedImage car;
        private BufferedImage road;

        private Set<Direction> directions = new TreeSet<>();

        private double directionOfRotation = 0;

        public TestPane() throws IOException {
            car = ImageIO.read(getClass().getResource("/images/Car.png"));
            road = ImageIO.read(getClass().getResource("/images/Road.png"));

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), InputAction.PRESSED_LEFT);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), InputAction.RELEASED_LEFT);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), InputAction.PRESSED_RIGHT);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), InputAction.RELEASED_RIGHT);

            am.put(InputAction.PRESSED_LEFT, new DirectionAction(Direction.LEFT, true));
            am.put(InputAction.RELEASED_LEFT, new DirectionAction(Direction.LEFT, false));
            am.put(InputAction.PRESSED_RIGHT, new DirectionAction(Direction.RIGHT, true));
            am.put(InputAction.RELEASED_RIGHT, new DirectionAction(Direction.RIGHT, false));

            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (directions.contains(Direction.RIGHT)) {
                        directionOfRotation += 1;
                    } else if (directions.contains(Direction.LEFT)) {
                        directionOfRotation -= 1;
                    }

                    // No doughnuts for you :P
                    if (directionOfRotation > 180) {
                        directionOfRotation = 180;
                    } else if (directionOfRotation < -180) {
                        directionOfRotation = -180;
                    }

                    repaint();
                }
            });
            timer.start();
        }

        protected void setDirectionActive(Direction direction, boolean active) {
            if (active) {
                directions.add(direction);
            } else {
                directions.remove(direction);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(213, 216);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            drawRoadSurface(g2d);
            drawCar(g2d);
            g2d.dispose();
        }

        protected void drawCar(Graphics2D g2d) {
            g2d = (Graphics2D) g2d.create();
            int x = (getWidth() - car.getWidth()) / 2;
            int y = (getHeight() - car.getHeight()) / 2;
            g2d.drawImage(car, x, y, this);
            g2d.dispose();
        }

        protected void drawRoadSurface(Graphics2D g2d) {
            g2d = (Graphics2D) g2d.create();
            // This sets the point of rotation at the center of the window
            int midX = getWidth() / 2;
            int midY = getHeight() / 2;
            g2d.rotate(Math.toRadians(directionOfRotation), midX, midY);
            // We then need to offset the top/left corner so that what 
            // we want draw appears to be in the center of the window,
            // and thus will be rotated around it's center
            int x = midX - (road.getWidth() / 2);
            int y = midY - (road.getHeight() / 2);
            g2d.drawImage(road, x, y, this);
            g2d.dispose();
        }

        protected class DirectionAction extends AbstractAction {

            private Direction direction;
            private boolean active;

            public DirectionAction(Direction direction, boolean active) {
                this.direction = direction;
                this.active = active;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                setDirectionActive(direction, active);
            }

        }

    }
}