2D-Grafik Shapes auf Zeichenfläche gleichmäßig verschieben

Sascha_S

Mitglied
Hallo, ich hoffe, dass ich mit meiner Frage hier an der richtigen Stelle bin. Ich habe schon tagelang gegoogelt und rumprobiert, aber irgendwie steh ich auf dem Schlauch.

Ich möchte gern ein bzw. mehrere Shape-Objekte von einer Position auf meiner Zeichenfläche zu einer anderen (diese wird per Mausklick bestimmt) verschieben. Die Verschiebung soll mit einer konstanten Geschwindigkeit entlang einer Geraden von Start- zu Zielpunk geschehen. Eigentlich sollte das doch nicht so schwer sein, oder? ???:L

Mein derzeitiger Ansatz ist, dass ich zunächst die Differenz der X- und X-Werte der aktuellen Position und der neuen Zielposition bestimme. Dann berechne ich mit Hilfe dieser Werte die Distanz, die mein Shape Objekt zurücklegen wird (Hypotenuse des Dreiecks, Länge des Vektors, wie auch immer man es nennen will). Jetzt teile ich diese Länge durch einen festen Wert, dieser Wert representiert die Geschwindigkeit mit der sich das Objekt bewegen soll. Dadurch erhalte ich einen Wert der mir angibt, wieviel der Strecke mein Objekt pro Zeiteinheit zurück legt. Jetzt dividiere ich die relativen X- und Y-Werte durch diesen neuen Wert, um mit deren Hilfe die neue Position des Shape-Objekts nach der aktuellen Zeiteinheit berechnen zu können. Die einzelnen Zeiteinheiten durchlaufe ich in einem Thread und nähere mich so immer weiter der Zielposition. (Oh man, ich hoffe ich konnte das halbwegs vernünftig ausdrücken.)

Soweit die Idee. Das funktioniert auch mehr oder weniger gut. Das Problem ist aber, dass das Objekt von seiner Linie (teilweise recht stark) abweicht. Dadurch wird irgendwann der Kurs korrigiert und das Objekt "schlägt einen Hacken". So kommt das Objekt zwar am Ende an seiner Position an, aber halt nicht ganz so, wie ich mit das vorgestellt hatte.

So sieht das dann aus:
movingsquare.png

Bild

Hier noch mal die Implementierung("rot" ist die Hauptklasse in der gezeichnet wird usw.):
Java:
class RefreshThread extends Thread {
	Rotating rot;

	public RefreshThread(Rotating rot) {
		this.rot = rot;
	}

	public void run() {
		
		double distanceX;
		double distanceY;
		double distance;
		int speed = 2;

		while (true) {
			try {
				sleep(15);
				if (rot.currentX != rot.targetX || rot.currentY != rot.targetY) {
					distanceX = rot.targetX - rot.currentX;
					distanceY = rot.targetY - rot.currentY;
					System.out.println("a = " + distanceX);
					System.out.println("b = " + distanceY);
					distance = Math.sqrt(distanceX*distanceX + distanceY*distanceY);
					System.out.println("c = " + distance);
					double factor = distance/speed;
					System.out.println("factor = " + factor);
					rot.currentX =  rot.currentX + new Double(distanceX / factor).intValue();
					rot.currentY =  rot.currentY + new Double(distanceY / factor).intValue();
					System.out.println(rot.currentX + ";" + rot.currentY);
				}				
			} catch (InterruptedException e) {
			}
			rot.doRepaint();
		}
	}
}


Woran könnte das liegen? Was mache ich falsch? Sind das nur Rundungsfehler? Wie kann ich das vermeiden? Fragen über Fragen 😛

Danke schonmal für die Hilfe 🙂
 
1. Double.valueOf benutzen
2. while(true) ist nie gut und birgt die Gefahr einer endloschleife
3. solltest du rot.repaint() aufrufen und diesen zum EDT synchronisieren
4. deine Rechnung hab mir ejtzt nicht angeschaut aber einmal hantierst du mit int rum und einmal mit double da kann es schon zu rundungsfehler kimmen
 
Zu 1. und 4.) Okay, vielen Dank für die schnelle Hilfe! Problem gelöst, funktioniert jetzt super 😀. Es lag offensichtlich wirklich nur an meinem Integer-Double-Mix und den daraus resultierenden Rundungsfehlern.

Zu 2.) Ich hab das while==true rausgenommen und durch ein Boolean ersetzt, welches die Schleife stopt, sobald das Objekt am Ziel angekommen ist. Ich starte jetzt immer einen neuen Thread sobald wieder ein neues Ziel angegeben wird.

Frage: Ist das auch besser so, wenn ich noch andere sich bewegende Objekte habe? Dann würde ich bei, sagen wir mal 5, sich bewegenden Objekten 5 Threads starten, anstatt einen Thread zu haben, der immer läuft. Gibt es noch andere Lösungsmöglichkeiten? ???:L

Zu 3.) Ich rufe jetzt repaint direkt auf (keine Ahnung, warum ich das vorher über eine extra Methode gemacht habe). Aber was genau meinst du mit synchronisieren? Irgendwie habe ich das mit dem Event Dispatcher Thread anscheinend noch nicht so recht verstanden 🙁

Hier nochmal der neue Code des Threads:
Java:
class RefreshThread extends Thread{
	Rotating rot;
	private boolean flag;

	public RefreshThread(Rotating rot) {
		this.rot = rot;
		flag = true;
	}

	public void hold() {
		flag = false;
	}

	public void run() {
		double distanceX;
		double distanceY;
		double distance;
		int speed = 2;
		while (flag == true) {
			System.out.println("running");
			try {
				sleep(15);

				if (rot.rect.getX() < rot.targetX - 1
						|| rot.rect.getX() > rot.targetX + 1
						|| rot.rect.getY() < rot.targetY - 1
						|| rot.rect.getY() > rot.targetY + 1) {
					distanceX = rot.targetX - rot.rect.getX();
					distanceY = rot.targetY - rot.rect.getY();
					System.out.println("a = " + distanceX);
					System.out.println("b = " + distanceY);
					distance = Math.sqrt(distanceX * distanceX + distanceY
							* distanceY);
					System.out.println("c = " + distance);
					double factor = distance / speed;
					System.out.println("factor = " + factor);
					rot.rect.setRect(rot.rect.getX() + distanceX / factor,
							rot.rect.getY() + distanceY / factor,
							rot.objectSize.getWidth(),
							rot.objectSize.getHeight());
					System.out.println(rot.rect.getX() + ";" + rot.rect.getY());
				} else {
					flag = false;
				}

			} catch (InterruptedException e) {
			}
			rot.repaint();
		}
	}
}
 
Über ein flag ist besser.
kann übrigens auch einfach nur
Code:
while (flag)
schreiben.

Mit synchroniseren mein, dass Zeichenoprtationen nur im EDT laufen dürfen.
Es gibt einige Methoden die sind von Haus aus synchronisert wenn man diese aufruft steht manchmal in der API 😉 bei repaint weiß ich es nicht musst mal n der API schauen
Ansonsten synchronisieren.

SwingUtilities#invokeLater ist das Stichwort 😉
 
ahja, danke euch beiden. ich hatte leider noch keine zeit weiter rumzuprobieren, aber prinzipiell funktionierts jetzt schon so wie's soll. vielleicht meld ich mich nochmal, wenn es noch probleme gibt, aber danke für die schnelle und professionelle hilfe 🙂
 

Neue Themen


Zurück
Oben