# Drehen eines Bildes relativ zur Mauszeigerposition



## Ribbley (17. Aug 2012)

Aaaalso, ich hab den ganzen Vormitag und nun auch schon die hälfte des Nachmittags damit verbracht, aber ich find nichts gescheites dazu.

Ich möchte erreichen das sich mein Bild mit der oberen Kante immer zum Cursor hindreht.
Das ganze hab ich mir gedacht, löse ich mit Vektoren, wie in der Mathematik und hab mir dazu einfach eine Klasse erstellt die eine X und eine Y koordinate speichern (und mit getX/Y wiedergeben) kann.

Wenn die Maus bewegt wird, werden ein x und ein y wert relativ zur mitte des bildes gespeichert.
(=> Maus in der Mitte -> Wert X==0 Y==0)
Anhand dieser Koordinaten und des Mittelpunktes (hab ich nur in pixeln) läst sich der Vektor berechnen.
Der Standartvektor ist praktisch die Y-achse eines koordinatensystems -> (0,1)
Die Rechnung sollte(?) stimmen, um einen Winkel rauszubekommen.

Das bei mehrmaliger Ausführung das ganze nicht hinhaut(und ist auch ein Spiel - dazu wird es kommen) weiß ich , aber ist nicht mein hauptproblem, das krieg ich dann schon hin, was mich wundert ist das ich keine Winkel <80 bekomme z.b. und teilweise ein NaN (Not a Number) beim double-wert des winkels. 

Hier der (hoffentlich ausreichende) quellausschnitt:

```
public void doLogic(long delta){
		super.doLogic(delta);
		//ROTATE PLAYER IF THETA != 0
		if(moved){
			playersight=new Vektor(xsight,ysight);
			//COMPUTE THETA!			
			double a = (playersight.getX()*standartsight.getX()+playersight.getY()*standartsight.getY())/
			(Math.sqrt(playersight.getX()*playersight.getX()+playersight.getY()+playersight.getY())*Math.sqrt(standartsight.getX()*standartsight.getX()+standartsight.getY()*standartsight.getY()));
			theta=Math.acos(Math.toRadians(a));
			System.out.println(Math.toDegrees(theta));
			rotatedplayer = rotateImage(player,10);
			moved=false;
		}else
			rotatedplayer = player;
```

DER GESAMTE CODE (der relevanten Klasse - für alle Fälle):


Spoiler





```
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;


public class Player 
extends Sprite implements MouseMotionListener{

	private static final long serialVersionUID = 1L;
	int speed = 60;
	Game p;
	
	BufferedImage player;
	BufferedImage rotatedplayer;
	Graphics2D playergraphics;
	
	Vektor standartsight;//(p.getWidth()/2)+2,(p.getHeight()/2)+8+1); = new Vektor(0,1);
	Vektor oldplayersight;
	Vektor playersight;
	double xsight,ysight;
	boolean moved=false;
	double theta=10;
	
	public Player(BufferedImage[] i, double x, double y, long delay, Game p) {
		super(i, x, y, delay, p);
		this.p=p;
		p.frame.addMouseMotionListener(this);
		standartsight =new Vektor(0,1);
		player = pics[currentpic];
	}
	
	public void doLogic(long delta){
		super.doLogic(delta);
		//ROTATE PLAYER IF THETA != 0
		if(moved){
			playersight=new Vektor(xsight,ysight);
			//COMPUTE THETA!			
			double a = (playersight.getX()*standartsight.getX()+playersight.getY()*standartsight.getY())/
			(Math.sqrt(playersight.getX()*playersight.getX()+playersight.getY()+playersight.getY())*Math.sqrt(standartsight.getX()*standartsight.getX()+standartsight.getY()*standartsight.getY()));
			theta=Math.acos(Math.toRadians(a));
			System.out.println(Math.toDegrees(theta));
			rotatedplayer = rotateImage(player,10);
			moved=false;
		}else
			rotatedplayer = player;
		
	}
	public void drawObjects(Graphics g) {
		//Player kriegt fixe x,y werte zum anzeigen!		
		g.drawImage(rotatedplayer, (int) (p.getWidth()/2-getWidth()/2), (int) (p.getHeight()/2-getHeight()/2), null);
		player= rotatedplayer;
	}
	private static BufferedImage rotateImage(BufferedImage src, double degrees) {
        AffineTransform affineTransform = AffineTransform.getRotateInstance(
                Math.toRadians(degrees),
                src.getWidth() / 2,
                src.getHeight() / 2);
        BufferedImage rotatedImage = new BufferedImage(src.getWidth(), src
                .getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
        g.setTransform(affineTransform);
        g.drawImage(src, 0, 0, null);
        return rotatedImage;
    }

	@Override
	public void mouseDragged(MouseEvent arg0) {		
	}

	@Override
	public void mouseMoved(MouseEvent arg0) {
		moved = true;
		xsight = arg0.getX()-(p.getWidth()/2)+2;
		ysight = arg0.getY()-(p.getHeight()/2)+8;
		System.out.println("Mouse move detected!("+xsight+"|"+ysight+")");
		
	}
}
```




PS: Sorry für 2 quelltexte, aber offensichtlich ging der spoilerbutton nicht in diesem forum/hab ihn nicht gefunden :x

MfG
Ribbley!


----------



## Marco13 (17. Aug 2012)

Die Berechung des benötigten Drehwinkels könnte man wohl mit Math.atan2(dy,dx) machen.

Aber mal unabhängig davon: Ich glaube, es ist KEINE gute Idee, bei jeder Drehung ein neues Bild zu erstellen. Stattdessen sollte vermutlich eher der aktuelle Drehwinkel des Spielers gespeichert werden. Dann kann man in der drawObjects das gedrehte Bild zeichnen. Letzteres würde dann grob so ablaufen, wie jetzt schon, als ETWA

```
AffineTransform oldAT = g.getTransform();
g.rotate(centerX, centerY, angle);
g.drawImage(...);
g.setTransform(oldAT);
```
aber eben OHNE jedes mal ein neues Bild zu erstellen.


----------



## stKev (20. Aug 2012)

Hi, habe vor einigen Wochen die selbe Idee gehabt. Wobei ich hier mit der AffineTransform gearbeitet habe die mir das bestehende Bild dreht. Wichtig bei der ganzen Sache ist nur das keiner der beiden Vektoren 0,0 werden sollte. Dies fange ich aber in der Methode(calculate) welche calcTheta berechnet ab. Sonst geschieht es, dass das Bild den Bezug verliert wo vorne ist. Um dein NaN Problem zu lösen. Es tritt auf wenn dein Mauszeiger im rotate Ursprung sich befindet.

Würde dir auch empfehlen dein Ursprung in die Mitte des Players zu verschieben.

JPanel:

```
package player;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Panel extends JPanel implements MouseMotionListener, MouseListener, KeyListener, Runnable {
	
	/* Attribute */
	private JFrame parent;
	private Player p;
	private Thread t;
	private boolean rotate;
	// MausPosi
	private Point mp;

	
	/* Konstruktor */
	public Panel(JFrame parent) {
		super();
		this.parent = parent;
		this.setFocusable(true);
		this.addKeyListener(this);
		this.addMouseMotionListener(this);
		this.addMouseListener(this);
		init();
	}
	/* initialisiert Objekte */
	private void init() {
		// Player mit Startkoordinaten
		p = new Player(100, 100);
		
		t = new Thread(this);
	
		// temp MousePosi bis zur ersten MouseTranslation
		mp = new Point(0,0);
		rotate = true;

		t.start();
	}
	
	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D) g;
		// zeichnet Player
		p.drawPlayer(g2d);
	}

	@Override
	public void run() {
		// GAMELOOP
		while (true) {

			// Berechnung + Bewegung
			p.calculate(mp);
			if (rotate) {
				p.rotate();
			}
			p.translate();
			repaint();

			try {
				Thread.sleep(25);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	/* keyPressed + keyReleased ändern die Translationattribute des Player in Abhängigkeit des Tastendrucks */
	@Override
	public void keyPressed(KeyEvent e) {
		if (e.getKeyCode() == KeyEvent.VK_A) {
			p.setDx(-15);
		}
		if (e.getKeyCode() == KeyEvent.VK_D) {
			p.setDx(15);
		}
		if (e.getKeyCode() == KeyEvent.VK_W) {
			p.setDy(-15);
		}
		if (e.getKeyCode() == KeyEvent.VK_S) {
			p.setDy(15);
		}	
	}
	
	@Override
	public void keyReleased(KeyEvent e) {
		if (e.getKeyCode() == KeyEvent.VK_A) {
			p.setDx(0);
		}
		if (e.getKeyCode() == KeyEvent.VK_D) {
			p.setDx(0);
		}
		if (e.getKeyCode() == KeyEvent.VK_W) {
			p.setDy(0);
		}
		if (e.getKeyCode() == KeyEvent.VK_S) {
			p.setDy(0);
		}
	}

	@Override
	public void keyTyped(KeyEvent e) {	
	}

	@Override
	public void mouseDragged(MouseEvent e) {
	}

	@Override
	public void mouseMoved(MouseEvent me) {
		// ermittelt den Point der MausPosi
		mp = me.getPoint();
	}
	@Override
	public void mouseClicked(MouseEvent arg0) {
		if (rotate == false) {
			rotate = true;
		}else{
			rotate = false;
		}
		
	}
	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void mousePressed(MouseEvent arg0) {
		
		
	}
	@Override
	public void mouseReleased(MouseEvent arg0) {
		
	}
}
```

Player

```
package player;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import javax.swing.ImageIcon;

public class Player {
	
	/* Attribute */
	// wird nur für die StartPosition und die Ermittlung des Centers benötigt
	// nach der Initialisierung werden x,y nicht mehr verändert bzw. anhand des Winkel verschoben
	private int x;
	private int y;
	// x,y NICHT VERWENDEN -----> this.center.getX()/getY() verwenden!!!

	
	private int dx;
	private int dy;
	private Image skin;
	private Image arrow;
	private Point2D center;
	private Point mp;
	
	private double theta;
	private double dt;
	private double calcTheta;
	private double anchorX;
	private double anchorY;
	
	private AffineTransform tx;

	/* Konstruktor */
	public Player(int x, int y) {
		this.x = x;
		this.y = y;
		init();
		
	}
	
	/* initialisiert Objekte */
	private void init() {
		tx = new AffineTransform();
		
		// setzt den Player auf die StartPosi
		tx.setToTranslation(this.x, this.y);

		// lädt die Bilder
		// Auto
		skin = new ImageIcon("Image\\tank.png").getImage();
		// Ursprung
		arrow = new ImageIcon("Image\\a.png").getImage();
		
		// verschiedt den rotations anchor in die mitte des Bildes
		anchorX = skin.getWidth(null) / 2;
		anchorY = skin.getHeight(null) / 2;
		
		// repräsentiert den Mittelpunkt des bewegten Objektes als Point2D
		center = new Point2D.Double(this.x + (skin.getWidth(null) / 2), this.y + (skin.getHeight(null) / 2));
	}
	// zeichnet
	public void drawPlayer(Graphics2D g2d) {
		Graphics2D g2d_p = (Graphics2D) g2d.create();
		//g2d_p.drawImage(arrow, (int)(this.center.getX() - arrow.getWidth(null) / 2), (int)(this.center.getY() - arrow.getHeight(null) / 2), null);
		g2d_p.drawImage(skin, tx, null);
		//g2d_p.setColor(Color.BLACK);
		//g2d_p.fill(new Rectangle((int)this.center.getX(), (int)this.center.getY(), 5, 5));
	}
	/*
	 * Die Methode erhält alle 25ms die neue Position der Maus, mit Hilfe dieser Position
	 * ermittelt sie den MausVektor (von Mittelpunkt des zu bewegenden Objektes bis zur Maus).
	 * Ein BezugsVektor der die Front des Objektes repräsentiert, hilft bei der Berechnung
	 * des Winkels(calcTheta), den diese beiden Vektoren aufspannen.
	 * Da calcTheta max. 1 Pi annehmen kann, wird am Ende der Methode insofern die Maus links vom Objekt ist
	 * der Winkel umgerechnet.
	 */
	public void calculate(Point mp) {
		// mousePosi
		this.mp = mp;
		
		int[] bezugsVek = {0,-10};
		int[] mVek = new int[2];
		
		// ermittelt den vektor vom mittelpunkt des Objektes zur Maus (quasi wird hier der Ursprung verschoben)
		mVek[0] = (int) (this.mp.x - this.center.getX());
		mVek[1] = (int) (this.mp.y - this.center.getY());
		
		// damit die Winkelberechnung nicht NaN ergibt, wird der MausVek der vom Mittelpunkt bis zur MausPosi zeigt
		// auf (1,1) gesetzt, insofern die Maus sich im Ursprung befindet
		if(mVek[0] == 0) {
			mVek[0] = 1;
		}
		if (mVek[1] == 0) {
			mVek[1] = 1;
		}
		
		// Skalarprodukt aus den Vektorkomponenten (berechnet den winkel zwischen dem bezugsVek und dem vektor der zur maus zeigt)
		calcTheta = Math.acos(
							  ((bezugsVek[0] * mVek[0]) + (bezugsVek[1] * mVek[1]))
														/
							  ((Math.sqrt((bezugsVek[0] * bezugsVek[0]) + (bezugsVek[1] * bezugsVek[1])))
													    *
							  (Math.sqrt((mVek[0] * mVek[0]) + (mVek[1] * mVek[1]))))
							 );
		
		// falls die maus link vom Objekt ist, wird die berechnung konvertiert, da calcTheta max 1 pi annimmt. (halberKreis)
		if (mp.x < this.center.getX()) {
			calcTheta = Math.toRadians(360) - calcTheta;
		}	
	}
	/*
	 * Diese Methode wird ebenfalls alle 25ms nach der calculate() in der run()(Gameloop) Methode aufgerufen.
	 * Sie lässt das Objekt in Abhängigkeit der Differenz des vorherigen Winkels(theta) und des aktuellen Winkels(calcTheta)
	 * rotieren. Dazu wird das AffineTransform Objekt, mit dessen Hilfe das Image des Objektes gezeichnet wird, verändert.
	 * Am Ende werden die Winkel noch gleichgesetzt.
	 */
	public void rotate() {
			// ermittelt die differenz des berechnet winkels und dem vorherigen
			dt = this.calcTheta - this.theta;
			System.out.println(Math.toDegrees(dt));
			tx.rotate(dt, anchorX, anchorY);
			
			// wurde das bild rotiert so werden beide Winkel gleichgesetzt, damit er nicht unendlich um den errechneten Winkel dreht
			// die calculate wird ebenfall im gameloop(25ms) ausgeführt (daher wir calcTheta sofort ein neuer wert zugewiesen)
			// theta enthält den Wert des zuvor berechneten Winkels, da bei dem nächsten Aufruf der Methode vom neu berechneten
			// Winkel der vorherige abgezogen werden muss, damit das Image nur um die Differenz gedreht wird.
			this.theta = calcTheta;
	}
	
	/*
	 * Verändert die Koordinaten des AffineTransform Objektes in Abhängigkeit von dx und dy, welche über
	 * Tastendruck verändert werden. Insofern eins ungleich 0 ist.
	 * Ebenfalls wir der Ursprung der Rotation in Abhängkeit zum Winkel(theta), dx und dy verschoben.
	 */
	public void translate() {
		// da die KeyRealeased 0 ja nur so spamed, hier eine prüfung eine bewegung um (0,0) sinnlos..
		if (this.dx != 0 || this.dy != 0) {

			tx.translate(dx, dy);
			// der Point2D ist inkompatibel mit der AffineTransform
			// somit alternativ Lösung um den Punkt in Abhänigkeit zum Winkel zuverschieben
			// das auto bewegt sich z.B. 15 pixel 45 grad geneigt
			// und der Ursprung würde ohne diese berechnung ungerichter 15 pixel nach oben bewegt..
			this.center.setLocation(this.center.getX() + (dx * Math.cos(this.theta) - dy * Math.sin(this.theta)),
									this.center.getY() + (dx * Math.sin(this.theta) + dy * Math.cos(this.theta)));
		}
	}

	/* GETTER & SETTER */

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getDx() {
		return dx;
	}

	public void setDx(int dx) {
		this.dx = dx;
	}

	public int getDy() {
		return dy;
	}

	public void setDy(int dy) {
		this.dy = dy;
	}

	public Image getSkin() {
		return skin;
	}

	public void setSkin(Image skin) {
		this.skin = skin;
	}

	public Image getArrow() {
		return arrow;
	}

	public void setArrow(Image arrow) {
		this.arrow = arrow;
	}

	public Point2D getCenter() {
		return center;
	}

	public void setCenter(Point2D center) {
		this.center = center;
	}

	public double getTheta() {
		return theta;
	}

	public void setTheta(double theta) {
		this.theta = theta;
	}

	public double getCalcTheta() {
		return calcTheta;
	}

	public void setCalcTheta(double calcTheta) {
		this.calcTheta = calcTheta;
	}

	public AffineTransform getTx() {
		return tx;
	}

	public void setTx(AffineTransform tx) {
		this.tx = tx;
	}
	
	
	public double getAnchorX() {
		return anchorX;
	}


	public void setAnchorX(double anchorX) {
		this.anchorX = anchorX;
	}


	public double getAnchorY() {
		return anchorY;
	}


	public void setAnchorY(double anchorY) {
		this.anchorY = anchorY;
	}
}
```

Window lasse ich mal weg. Falls du das für ein Spiel verwenden willst, sage ich dir gleich, wirst du relativ wenige Sprites finden. 2D overview ohne Vogelperspektive ist rare.

Edit: Ah, sehe grade du hast ja ebenfall AffineTransform verwendet. Klappt deins den so wie du es dir erwünscht hast oder hast du noch Probleme dabei?


----------

