简体   繁体   中英

java, rotate BufferedImage with mouse angle

I have been searching all over the internet for a simple way to just rotate a sprite following an angle.

The objective is to have a weapon sprite following the mouse by rotating at the centre of the screen (Top-down shooter in Java). I have tried different things:

NB: The render(Graphics g) function is inside my Gun.java class and uses g , the Graphics element I use to paint on the canvas of the game. The image is the BufferedImage containing the original sprite. And reticle.getAngle() is giving the angle made by the mouse considering the centre of the screen as the origin of the frame.

Attempt 1

public void render(Graphics g) {
        // create a new BufferedImage with the image of the gun on it
        BufferedImage rotatedImage = new BufferedImage(image.getWidth(), image.getHeight(),
            BufferedImage.TYPE_INT_ARGB);
        Graphics gRotatedImage = rotatedImage.getGraphics();
        gRotatedImage.drawImage(image, 0, 0, null);

        // rotate this gun image in the direction of shoot
        private AffineTransform at = new AffineTransform();
        at.rotate(reticle.getAngle() + Math.PI,
            rotatedImage.getWidth()/2, rotatedImage.getHeight()/2);

        AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
        rotatedImage = op.filter(rotatedImage, null);

        // finally display the rotated version of the gun image
        g.drawImage(rotatedImage,
            (int)(handler.getDisplay().getWidth()/2 - rotatedImage.getWidth()/2), 
            (int)(handler.getDisplay().getHeight()/2 - rotatedImage.getHeight()/2), 
            rotatedImage.getWidth(), rotatedImage.getHeight(), null);

}

With this solution, from java2s.com , I end up with the sprite being displayed at the centre and rotating but more like a helicopter... It keeps rotating not following the mouse.

I also tested all the solutions from the related StackOverflow question . This time, I get the weapon being displayed at the top left corner following the mouse, but I can't find a way to place it at the centre of the screen. I tried translations but then the sprite image rotates considering the top left corner as the centre.

Attempt 2

public void render(Graphics g) {
        Graphics2D g2d = (Graphics2D) g.create();

        double rotation = 0f;

        int width = image.getWidth() - 1;
        int height = image.getHeight() - 1;

        rotation = reticle.getAngle();
        rotation = Math.toDegrees(rotation) + 180;

        g2d.rotate(Math.toRadians(rotation), width / 2, height / 2);
        // g2d.translate(handler.getDisplay().getWidth()/2, handler.getDisplay().getHeight()/2);
        g2d.drawImage(image, 0, 0, null);

        int x = width / 2;
        int y = height / 2;
        g2d.setStroke(new BasicStroke(3));
        g2d.setColor(Color.RED);
        g2d.drawLine(x, y, x, y - height / 4);
        g2d.dispose();
}

I just would like to rotate my sprite every tick of the game by the angle provided by reticle.getAngle() which I know is good. I feel really lost on how to use Graphics2D or AffineTransform to perform rotation. Can someone provide an example on how to rotate a sprite following the mouse and then display it at the centre of the screen?

What is the best way to rotate an image which we then want to display at the centre of the screen?

I don't know if this helps, but I wrote this example program, which has a rectangle (works the same as an image really...) at the center of the screen which rotates following your mouse. The important stuff is in the paintComponent method, pretty much everything else is setup.

package main;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class GPanel extends JPanel{
    private int width, height;
    private Timer timer;
    private int mouseX, mouseY;

    public static void main(String[] args) {
        JFrame f = new JFrame();
        GPanel gp = new GPanel(400, 400);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(gp,BorderLayout.CENTER);
        f.pack();
        f.setVisible(true);
    }

    public GPanel(int width, int height) {
        mouseX = 0;
        mouseY = 0;
        this.width = width;
        this.height = height;
        this.setPreferredSize(new Dimension(width, height));

        timer = new Timer(17 , new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
            // TO DO
                repaint();
            }
        });
        addMouseMotionListener();
        timer.start();
    }

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        g.setColor(new Color(255, 255, 255));
        g2.fillRect(0, 0, width, height);
        g2.setColor(new Color(0, 0, 0));
        g2.translate(width / 2, height / 2);
        double x = mouseX - width / 2d;
        double y = mouseY - height / 2d;
        double theta = Math.atan2(x, y);
        g2.rotate(-theta);
        g2.translate(-20, 0);
        g2.fillRect(0, 0, 40, 100);
    }

    private void addMouseMotionListener() {
        this.addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
                mouseX = e.getX();
                mouseY = e.getY();
            }

            @Override
            public void mouseDragged(MouseEvent e) {
                if(SwingUtilities.isLeftMouseButton(e)) {
                    //TO DO
                } else if(SwingUtilities.isRightMouseButton(e)) {

                }
                repaint();
            }
        });
    }
}

What I would do is create an affine transformation for the sprite. I imagine a sprite, img should be at (cx, cy) and the angle it is rotated should be theta .

Graphics2D g2d = (Graphics2D)g;

AffineTransform at = new AffineTransform();
//translate the center to be at cx, cy.
at.translate(cx - img.getWidth()/2.0, cy - img.getHeight()/2.0);
//rotate about the center of the sprite.
at.rotate(theta, img.getWidth()/2.0, img.getHeight()/2.0);
g2d.drawImage(img, at, this);

I tested it out with this example, where you can click on the JPanel and center the sprite. The sprite follows the mouse cursor around when you move it.

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
public class RotateArrow{
    int cx = 0;
    int cy = 0;
    double theta = 0;
    public void startGui(){
        JFrame frame = new JFrame("arrow");

        BufferedImage img = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);

        Graphics g = img.getGraphics();
        g.setColor(Color.RED);
        g.drawLine(0, 32, 64, 32);
        g.drawLine(48, 0, 48, 64);
        g.dispose();

        JPanel panel = new JPanel(){

            @Override
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D)g;

                AffineTransform at = new AffineTransform();
                //rotate about center of image.
                at.translate(cx - img.getWidth()/2.0, cy - img.getHeight()/2.0);

                at.rotate(theta, img.getWidth()/2.0, img.getHeight()/2.0);


                g2d.drawImage(img, at, this);

            }
        };

        panel.addMouseListener( new MouseAdapter(){
            @Override
            public void mousePressed(MouseEvent evt){
                cx = evt.getX();
                cy = evt.getY();
                panel.repaint();
            }
        } );

        panel.addMouseMotionListener( new MouseAdapter(){
            @Override
            public void mouseMoved(MouseEvent evt){
                double dx = evt.getX() - cx;
                double dy = evt.getY() - cy;
                if(dx != 0 || cy != 0){
                    theta = Math.atan2(dy, dx);
                    panel.repaint();
                }
            }
        } );

        frame.setContentPane(panel);
        frame.setSize(512, 512);
        frame.setVisible(true);


    }

    public static void main(String[] args){
        EventQueue.invokeLater( ()->new RotateArrow().startGui() );
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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