Hallo!
Ich möchte in C# ein Objekt1 auf Objekt2 zugehen lassen. Gegeben sind also die Koordinaten von Objekt1 und Objekt2 und die Geschwindigkeit von Objekt1. Dazu suche ich eine Formel, mit der ich die Position von Objekt1 zum Zeitpunkt t herausfinden kann.
Mein Ansatz wäre:
cos a = (X1 * X2 + Y1 * Y2) / (sqrt(X1² + Y1²) * sqrt(X2² + Y2²))
X1 = X1 - X1 * t * v * cos a
Y1 = Y1 - Y1 * t * v * cos a
bzw. in C#-Code:
double CosWinkel = (PosX * ZielX + PosY * ZielY) /
(Math.Sqrt(Math.Pow(PosX, 2) + Math.Pow(PosY, 2)) *
Math.Sqrt(Math.Pow(ZielX, 2) + Math.Pow(ZielY, 2)));
PosX = (PosX - PosX * ZeitpunktBewegung * Speed * CosWinkel);
PosY = (PosY - PosY * ZeitpunktBewegung * Speed * CosWinkel);
Allerdings haut das irgendwie nicht hin. Das Ergebnis ist, dass Objekt1 immer nach links oben fährt, unabhängig vom Ziel (was eigentlich durch das cos a verhindert werden soll). Ich habe versucht, die Formel durch Probieren herauszufinden, was aber an meinen geringen Physikkenntnissen gescheitert ist.
Wenn mir jemand hier helfen könnte, wäre das janz doll. Danke.
-> Informatik-Infotainment <-
Hab die Formel jetzt selber gar nicht untersucht -- aber ein häufiger Fehler ist, dass mit int werten gerechnet wird -- sind bei dir PosX, ZielX, PosY und ZielY alle vom Typ double?
Frohes Neues 🙂
Frohes Neues 🙂
Dir auch! 🙂
Hab die Formel jetzt selber gar nicht untersucht -- aber ein häufiger Fehler ist, dass mit int werten gerechnet wird -- sind bei dir PosX, ZielX, PosY und ZielY alle vom Typ double?
Ja, sind alle double. Es stimmt: Zuvor habe ich int benutzt, was zu merkwürdigen Fehlern geführt hat.
-> Informatik-Infotainment <-
Stell erstmal den Richtungsvektor 0X2-0X1 auf.
Aus diesem Vektor holst du dir mit ArcTan den Winkel phi des Richtungsvektors.
Für deine zurückgelegte Wegstrecke: v*t (eh klar)
Die !Komponenten! der der Bewegung:
x=vtcos(phi)
y=vtsin(phi)
zu deinem 0X1 komponentenweise addiert erhälts du den Ortsvektor deines bewegten Punktes.
Hoffe ich konnte dir weiterhelfen bei diesem Dynamikbeispiel.
Mein Ansatz wäre:
cos a = (X1 * X2 + Y1 * Y2) / (sqrt(X1² + Y1²) * sqrt(X2² + Y2²))
...
Ich hab keine Ahnung, wie Du auf diese Formel kommst und ob diese annaehrend korrekt sein kann. Fuer mich persoenlich muss grundsaetzlich ein Minus-Zeichen in einer Formel auftauchen, wenn aus Koordinatenpaaren ein Winkel berechnet werden soll.
Gruesse,
N1ls
@O5IRI5:
Danke für deinen ausführlichen Hinweis! Leider habe ich überhaupt keine Ahnung von Vektoren, sonst hätte ich selber versucht es mit Vektoren zu berechnen.
@N1ls:
Mein Ansatz wäre:
cos a = (X1 * X2 + Y1 * Y2) / (sqrt(X1² + Y1²) * sqrt(X2² + Y2²))
...Ich hab keine Ahnung, wie Du auf diese Formel kommst und ob diese annaehrend korrekt sein kann. Fuer mich persoenlich muss grundsaetzlich ein Minus-Zeichen in einer Formel auftauchen, wenn aus Koordinatenpaaren ein Winkel berechnet werden soll.
Gruesse,
N1ls
Hallo N1ls,
die Formel habe ich von hier. Ich habe lediglich die Z-Dimension vernachlässigt.
Grüße,
Jack
-> Informatik-Infotainment <-
Leider habe ich überhaupt keine Ahnung von Vektoren, sonst hätte ich selber versucht es mit Vektoren zu berechnen.
Hast du implizit ja schon gemacht.
cos a = (X1 * X2 + Y1 * Y2) / (sqrt(X1² + Y1²) * sqrt(X2² + Y2²))
Mit dem Skalarprodukt kannst du Winkel berechnen, aber wenn ich das richtig interpretiere errechnest du den Winkel zwischen den beiden Punkten. Also der eine Vektor zeigt vom Ursprung auf Punkt O1 und der zweite vom Ursprung auf Punkt O2
dein Winkel ist der dazwischen.
Liegt vieleicht eine verwechslung zwischen Gradmaß und Bogenmaß vor?
Gruss
tscherno
DISCLAIMER:
Ich weis granicht ob dass in diesem zusammenhang relevant ist da ich keien Ahnung von Mathe habe, will aber darauf hinweisen das die Math Methoden alle Winkel im Bogenmaß erwarten. Wenn ich also schwachsinn schreibe dann bitte ignorieren. 😉
double CosWinkel = (PosX * ZielX + PosY * ZielY) /
(Math.Sqrt(Math.Pow(PosX, 2) + Math.Pow(PosY, 2)) *
Math.Sqrt(Math.Pow(ZielX, 2) + Math.Pow(ZielY, 2)));
double Winkel = Math.Acos(CosWinkel);
Ich habe mit mal das Ergebnis von "Winkel" ausgeben lassen. Das scheint irgendwie nicht zu stimmen. Wenn das Ziel die Koordinaten 1/1 hat und Position die Koordinaten 400/400, muss logischerweise der Winkel 45° sein, bzw. 315°. Ich bekomme aber als Ergebnis 1,49 * 10 hoch -8! (Als Bogenmaß interpretiert wäre der Winkel immerhin noch 8,5 * 10 hoch -7)
edit:
Oh, wow! Ich glaube, ich hab's jetzt! Ein Freund hat mich auf die Formel
double TanWinkel = (PosY - ZielY) / (PosX - ZielX);
double Winkel = Math.Atan(TanWinkel);
aufmerksam gemacht. Und siehe da: Wenn ich das Ergebnis als Bogenmaß interpretiere, bekomme ich einen Winkel von 45 Grad raus!
-> Informatik-Infotainment <-
Mit dieser Formel
PosX = PosX - (ZeitpunktBewegung * Speed * CosWinkel);
PosY = PosY - (ZeitpunktBewegung * Speed * SinWinkel);
habe ich das Problem, dass mein Objekt zuerst wendet, bevor es das Ziel ansteuert, und dabei an Geschwindigkeit zunimmt.
Mein Programm (.exe-Datei) (Position ist 400/400, Ziel ist 600/1)
-> Informatik-Infotainment <-
Oh, wow! Ich glaube, ich hab's jetzt! Ein Freund hat mich auf die Formel
double TanWinkel = (PosY - ZielY) / (PosX - ZielX); double Winkel = Math.Atan(TanWinkel);
aufmerksam gemacht. Und siehe da: Wenn ich das Ergebnis als Bogenmaß interpretiere, bekomme ich einen Winkel von 45 Grad raus!
Ahh 🙂 Da ist das von mir vermisste Minus-Zeichen 🙂
Du bist definitiv auf dem richtigen Weg, aber Vorsicht! Sobald beide Objekte die gleiche X-Koordinate haben, laeufst Du in eine Exception. Den Sonderfall musst Du behandeln.
Stell erstmal den Richtungsvektor 0X2-0X1 auf.
Aus diesem Vektor holst du dir mit ArcTan den Winkel phi des Richtungsvektors.
Für deine zurückgelegte Wegstrecke: v*t (eh klar)
Die !Komponenten! der der Bewegung:
x=vtcos(phi)
y=vtsin(phi)zu deinem 0X1 komponentenweise addiert erhälts du den Ortsvektor deines bewegten Punktes.
Hoffe ich konnte dir weiterhelfen bei diesem Dynamikbeispiel.
Aber unbedingt darauf achten, dass dein Vektor v normiert ist!
Aber unbedingt darauf achten, dass dein Vektor v normiert ist!
Wie kommst du darauf?
LG
Harry
Wie kommst du darauf?
Weil du, um die r(x,y) Koordinate zu rechnen korrekterweise hingehst und
x(t)=vtcos(phi)
ausrechnest (y analog).
Wenn v nicht normiert ist, so ist t nicht deine einzige Laufvariable, weil die Norm von v abhängig ist von den Start- und Zielkoordinaten.
Hallo.
Ich habe jetzt einen neuen Ansatz, nachdem Cosinus und Sinus zu merkwürdigem Verhalten geführt haben:
PosX = StartPosX + StartPosX * Speed * Zeit;
PosY = StartPosX + StartPosY * Speed * Zeit * Steigung;
Doch das führt auch zu einem merkwürdigen Problem. Das Objekt steuert das Ziel richtig an, aber nach ca. 3/4 der Strecke nimmt das Objekt zufällige Positionen an. Es "tanzt wild herum", sozusagen. Ich kann mir dieses Verhalten nicht erklären.
Und noch eine dumme Frage:
double Steigung;
if (DeltaX() != 0)
Steigung = DeltaY() / DeltaX();
else
???
Wie behandle ich die Steigung, wenn DeltaX gleich Null ist?
Danke,
Jack
-> Informatik-Infotainment <-
nachdem Cosinus und Sinus zu merkwürdigem Verhalten geführt haben
Inwiefern?
Die Sache mit deiner Steigung klappt so nicht, da du die Geschwindigkeit nur abhängig von der x-Richtung machst. Je grösser die Steigung, desto schneller wird sich also dein Objekt bewegen... Lediglich die x-Geschw. bleibt konstant. Und das sollte auch deine Frage beantworten, wenn vx=0 wird... Dann geht die Steigung ins Unendliche.
Bleib also lieber bei der trigonometrischen Lösung.
Oh, wow. Ich habe es endlich hinbekommen! Ich behaupte einfach mal, dass ich das Problem jetzt gelöst habe, lasse mich aber gerne eines Besseren belehren. Wenn meine Lösung jetzt stimmt, schreibe ich nachträglich noch einen (längeren?) Beitrag, für all diejenigen, die mit den gleichen Problemen zu kämpfen haben werden, und gehe dabei auf meine gemachten Fehler ein.
Die entscheidende Stelle sieht jetzt so aus:
double NextPosX;
double NextPosY;
// Formeln müssen für größeres / kleineres Ziel angepasst werden
if (PosX < ZielX)
NextPosX = StartPosX + StartPosX * Speed * Zeit * Math.Cos(WertWinkel);
else
NextPosX = StartPosX - StartPosX * Speed * Zeit * Math.Cos(WertWinkel);
if (PosY < ZielY)
NextPosY = StartPosY + StartPosY * Speed * Zeit * Math.Sin(WertWinkel);
else
NextPosY = StartPosY - StartPosY * Speed * Zeit * Math.Sin(WertWinkel);
// wenn das Ziel zwischen der aktuellen und der nächsten Position liegt
if ((ZielX >= PosX && ZielX <= NextPosX) || (ZielY >= PosY && ZielY <= NextPosY) ||
(ZielX <= PosX && ZielX > NextPosX) || (ZielY <= PosY && ZielY > NextPosY))
{
ZielWurdeErreicht();
return true;
}
PosX = NextPosX;
PosY = NextPosY;
return false;
Was ich allerdings noch nicht behoben habe, ist, wie man vorgeht, wenn DeltaX 0 wird:
if (DeltaX() != 0)
Steigung = DeltaY() / DeltaX();
else
Steigung = ???
Wenn mir hier noch jemand auf die Sprünge helfen könnte, wäre das sehr nett.
Jack
-> Informatik-Infotainment <-
Ich versteh nicht recht... Die Steigung geht doch gar nicht mehr in deine Berechnung ein.
Jedenfalls ist y/x ein Grenzwertproblem, für x->0
Du musst also den Limes bilden
lim y/x
x->0+
0+ heisst hier dass sich x aus dem Positiven der 0 nähert. Negative x-Werte hast du ja in deinem Bsp. denk ich nicht, sonst musst du das auch berücksichtigen.
Dein y (also eigentlich dein DeltaY... habs hier nur verkürzt darstellen wollen), kann aber sehr wohl positiv oder negativ sein (die Gerade ist also steigend oder abfallend). Du brauchst hier also eine Fallunterscheidung. Was Positives geteilt durch eine "positive" Null macht +Unendlich. Was Negatives durch 0+ macht folglich -Unendlich.
Ich versteh nicht recht... Die Steigung geht doch gar nicht mehr in deine Berechnung ein.
Du hast recht. Daran habe ich gar nicht mehr gedacht. Richtig lösen konnte ich das Problem nicht. Dafür müsste ich erst wieder meine Mathekenntnisse auffrischen. Schon dieses vermeintlich einfache Problem hat jetzt vorerst genug mathematisches und physikalisches Wissen von mir abverlangt.
Die Lösung:
Ich konnte das Ganze nun entgültig lösen. Die Formel war übrigens nicht ganz richtig. Richtig ist die Formel so (im Vergleich zur vorherigen falschen Formel nähme die Geschwindigkeit konstant zu):
if (PosX < ZielX)
NextPosX = StartPosX + Speed * Zeit * CosWinkel;
else
NextPosX = StartPosX - Speed * Zeit * CosWinkel;
if (PosY < ZielY)
NextPosY = StartPosY + Speed * Zeit * SinWinkel;
else
NextPosY = StartPosY - Speed * Zeit * SinWinkel;
Ich versuche hier noch mal grob meine Gedanken zu dokumentieren, damit andere Anfänger und Fortgeschrittene mit dem selben Problem zumindest mal ein paar Ausgangspunkte haben.
Das Problem war, dass ich im Rückblick auf meinen ersten Ansatz sehr viele Dinge falsch angegangen bin, und wenn man nur einen Fehler behebt, die Bewegung noch immer total unlogisch verläuft. Deshalb ist es schwer, eine Fehlerquelle allein durch Ausprobieren auszuheben.
Wie man den Winkel zwischen zwei Punkten herausfinden kann:
double TanWinkel;
if (DeltaX() == 0)
TanWinkel = 0;
else
TanWinkel = DeltaY() / DeltaX();
double Winkel = Math.Atan(TanWinkel);
Wichtig ist hierbei zu schauen, dass DeltaX nicht Null werden darf. DeltaX und DeltaY steht jeweils für die Differenz zwischen der X- bzw. Y-Koordinate von Position und Ziel. Der Cosinus und Sinus des Winkels fließen dann in die obere Formel ein. Einfachheitshalber mache ich Folgendes:
if (DeltaX() == 0)
{
CosWinkel = 0;
SinWinkel = 1;
}
if (DeltaY() == 0)
{
CosWinkel = 1;
SinWinkel = 0;
}
Das bedeutet, dass wenn das Ziel sich nur in einer der beiden Achsen vom Ursprung unterscheidet, die betroffene Koordinate konstant bleibt.
Beispiel:
Aktuelle Position ist 100/200 und das Ziel hat die Koordinaten 100/400. Das Objekt würde sich also nur auf der X-Achse verschieben. Es wandert also nach rechts, bildlich gesprochen. Damit wird DeltaX = 0. Nach meinem Code wird damit auch der Cosinus = 0, was dazu führt, dass die Formel
NextPosX = StartPosX + Speed * Zeit * CosWinkel;
immer den Wert der Startposition behält. Im Fall des Beispiels 100.
Was genau ist die Zeit?
Die Zeit in der Formel ist die Zeit, die seit dem Start der Bewegung vergangen ist. In meinem Beispiel lasse ich die Bewegung erst nach einer Sekunde starten. Dementsprechend fängt die Zeit auch erst an, nachdem eine Sekunde vergangen ist:
private double ZeitSeitBewegung(bool Set)
{
if (Set)
{
if (ZeitpunktBewegung == 0)
ZeitpunktBewegung = ClassGlobal.SITime();
}
double ZeitSeitBeweg = ClassGlobal.SITime() - ZeitpunktBewegung;
return ZeitSeitBeweg;
}
SiTime() ist die Zeit, die seit Programmstart vergangen ist, in Sekunden. ZeitpunktBewegung ist ein Feld des Objekts.
Ich habe das Setzen der Zeit mit einer Variable "Set" geknüpft, da die Funktion später noch mal aufgerufen wird, um die aktuelle Zeit auszugeben, aber hier die Zeit nicht gesetzt werden soll.
Problem: Mein Objekt schießt über das Ziel hinaus und verhält sich danach komisch.
Um dieses Problem abzufangen, muss überprüft werden, ob das Objekt nicht im nächsten Schritt über das Ziel hinausschießt. Das Ziel pixelgenau zu treffen ist sehr unwahrscheinlich. Daher folgender Code:
if (CosWinkel == 0)
{
if ((ZielY >= PosY && ZielY <= NextPosY) || (ZielY <= PosY && ZielY > NextPosY))
{
ZielWurdeErreicht();
return true;
}
}
else if (SinWinkel == 0)
{
if ((ZielX >= PosX && ZielX <= NextPosX) || (ZielX <= PosX && ZielX > NextPosX))
{
ZielWurdeErreicht();
return true;
}
}
else
{
// wenn das Ziel zwischen der aktuellen und der nächsten Position liegt
if ((ZielX >= PosX && ZielX <= NextPosX) || (ZielY >= PosY && ZielY <= NextPosY) ||
(ZielX <= PosX && ZielX > NextPosX) || (ZielY <= PosY && ZielY > NextPosY))
{
ZielWurdeErreicht();
return true;
}
}
Die Fallunterscheidung hängt mit dem oben beschriebenen Fall zusammen, dass sich das Objekt nur auf einer Achse bewegt. Für einen solchen Fall testen wir einfach, ob das Objekt mit dem nächsten Schritt auf der anderen Achse das Ziel überschreiten würde. Ansonsten wird geprüft, ob das Ziel auf einer der beiden Achsen überschritten werden würde. Normalerweise sollte es auf beiden Achsen gleichzeitig überschritten werden. Aber das trifft wahrscheinlich nur bei sehr kleinen (oder großen?) Bewegungseinheiten zu.
Ich hoffe, ich konnte meine Überlegungen verständlich machen, und dieser Beitrag dem ein oder anderen hilft. Ich hoffe auch, dass auf meiner Seite diesbezüglich keine weiteren Probleme mehr auftreten.
Jack
edit: Ach ja. Und Danke noch mal an alle, die mir hier geholfen haben! Ohne euren "Stupps in die richtige Richtung zur richtigen Zeit" hätte ich das wahrscheinlich nie hinbekommen und aufgegeben. 😉
-> Informatik-Infotainment <-