Seite 1 von 1

Nichtlineare Bewegung

Verfasst: 26.03.2009, 18:35
von Zudomon
Hi!

Vielleicht könnt ihr mir auch bei diesem Problem helfen:
Eine lineare Bewegung auf die Zeit abzustimmen ist ja kein Problem... da wird ja einfach das Timedelta mit dem Wert multipliziert.
Aber wie ist es, wenn eine Art Abbremsung realisieren möchte:
v = v * 0.9

Wie bekomme ich da meine Timedelta mit rein? Gibt es dafür eine einfache Lösung oder muss ich da schon mit Beschleunigung und Geschwindigkeit rann?

MfG
Zudomon

Re: Nichtlineare Bewegung

Verfasst: 26.03.2009, 18:48
von TomCat
Bei einer konstanten Beschleunigung (Abbremsung ist auch ne Beschleunigung, nur andere Richtung, Musst Du einfach die Geschwindigkeit pro Zeiteinheit um immer den gleichen Wert add/subtrahieren) Die höhe des Wertes ergibt sich aus Zeitdelta mal gewünschter Beschleunigung.

ciao,
TomCat

Re: Nichtlineare Bewegung

Verfasst: 26.03.2009, 19:10
von knivil
Wie sieht denn deine Bewegungsgleichung aus? Also hast du mehr anzubieten als v = 0.9 * v? Wie gross ist denn dein dt, fuer das die Gleichung gilt.

Re: Nichtlineare Bewegung

Verfasst: 26.03.2009, 19:15
von Zudomon
Mehr anzubieten habe ich nicht... bis jetzt habe ich sowas halt benutzt, um die Kamerabewegung etwas gleiten zu lassen. Da kam es dann auf eine perfekte Gleichung nicht an, sondern sollte einfach nur gut aussehen. Aber wenn die FPS steigen, dann ist die Bewegung natürlich verfälscht. Aber was TomCat sagt, hört sich gut an... das werde ich probieren... aber erstmal noch ein paar andere Probleme lösen ;)

Re: Nichtlineare Bewegung

Verfasst: 26.03.2009, 19:23
von CodingCat
Hm, ich verstehe dein Problem nicht. Wenn du eine bestimmte Geschwindigkeit v hast, dann berechnest du deine nächset Position wohl mit x += v * d_t, wobei d_t die verstrichene Zeit seit dem letzten Frame ist. Selbes kannst du nun auf die Geschwindigkeit anwenden, d.h. du behälst die Bewegungsgleichung bei und veränderst zusätzlich jedes Frame v. Wenn du lineare Beschleunigung haben willst, geht das analog zur Bewegung mit v += a * d_t, wobei a für Bremsen natürlich negativ wäre, für Beschleunigen positiv. Alternativ könntest du auch noch exponentielle Beschleunigung / Bremsung versuchen, mit v *= pow(f, d_t). Das wirkt als ganz cool, wenn du die Geschwindigkeit sehr schnell anpassen möchtest, aber trotzdem einen weichen Übergang beibehalten willst.
Diese Art der Beschleunigung / Bremsung habe ich z.B. hier eingesetzt: http://vimeo.com/3435479 (Video, eher gegen Ende sichtbar)

Re: Nichtlineare Bewegung

Verfasst: 26.03.2009, 19:29
von Zudomon
Ich schätze mal, dass da auch kein echtes Problem hinter steckt. Hab halt nur noch nicht so ganz den zusammenhang verstanden gehabt...
Werde das nun mal versuchen umzusetzen!

Dein Video ist sehr nice... vor allem das SSAO sieht ganz gut aus! :D

Re: Nichtlineare Bewegung

Verfasst: 26.03.2009, 21:01
von Schrompf
Die pow-Methode ist die korrekte. Wenn Du vorher pro meinetwegen 20ms ein v *= 0.9 ausgeführt hast, wäre die korrekte Formel für beliebige Zeitdifferenzen ein v *= pow( 0.9, zeitdiff / 0.02sek)

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 09:26
von Zudomon
Ich hatte das gestern noch probiert mit dem, was vorher gesagt wurde. Das ganze kann man ja eigentlich für Partikelsimulationen verwenden. Allerdings klappt das bei der Kamerasteuerung überhaupt nicht.

Die Lösung von Schrompf ist genau das, was ich brauchte!! Ich hatte mir damals eine Lösung erarbeitet, die fast genauso aussah:
v *= pow(0.9, zeitdiff)
Allerdings funktionierte das auch nur beschränkt. Ich hatte da zwar erkannt, dass man jede Sekunde das v mit 0.9 multipliziert, aber um das schneller schwinden zu lassen habe ich dann die 0.9 immer kleiner werden lassen... z.B. auf 0.00001... so richtig funktionierte das dann alles nicht mehr...
Das man zeitdiff einfach noch durch die gewünschte Schrittweite teilt, darauf bin ich nicht gekommen! Danke dafür! :D


===== NACHTRAG =====

Nein, zu früh gefreut... komischerweise ist das alles immer noch Frameabhängig.
Ich beschreibe mal, was ich mache und zwar beim strafen mit der Maus:

Code: Alles auswählen

  if MButtonL and MButtonR and not MButtonM then begin
    CamMove.add(AxisHorizontal.GetAxisY * yPosRelative * -speedP);
    CamMove.add(AxisHorizontal.GetAxisX * xPosRelative *  speedP);
  end;

  xPosRelative:=0;
  yPosRelative:=0;

  l := power(lng(CamMove),0.3)*0.25;
  Campos := Campos + CamMove * l;
  CamMove.scl(power(0.5, Clock.Delta / 0.01 ));

//  sleep(20);

In CamMove wird das Mausdelta übertragen und skaliert. Anschließend das Mausdelta auf 0 gesetzt. Dann berechne ich mir ein l aus der Länge von CamMove. Dieser Wert wird mit CamMove multipliziert. Das ganze ist dafür da, damit man sich weiter fortbewegt, wenn man die Maus schneller bewegt.
Anschließend wird das CamMove runterskaliert mit der von Schrompf vorgeschlagenen Formel.

Ich habe da ( bis jetzt noch ) über 1000 FPS und das ganze klappt perfekt... Wenn ich allerdings das "sleep(20)" da rein mache, wird meine Bewegung zu langsam. Also das fühlt sich dann garnicht mehr an, wie vorher.

Weiß noch jemand Rat?

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 12:08
von Schrompf
Das hat ja mit der Dämpfung aus dem Orginalbeitrag nix mehr zu tun. Du hast schlicht vergessen, auch bei CamPos += irgendwas * CamMove noch die Zeitdifferenz einzuberechnen. Auch CamMove muss mit der Zeitdifferenz skalieren.

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 12:20
von Zudomon
Stimmt, mit der Dämpfung hat das nichts mehr zu tun und müsste auch perfekt funktionieren.

Die Frage ist nun nur noch, wie ich das ganze Konstrukt Zeitabhängig bekomme.
Du sagst, CamMove muss mit der Zeitdifferenz skalieren.
Aber wie? CamMove ist ja von der Maus abhängig. Also angenommen man bewegt die Maus um 20 Pixel, das wird dann in einem Frame gemessen und wieder zurückgesetzt. Nun rechnet man die Zeitdifferenz in CamMove mit ein. So würden z.B. 3 Pixel an effektivbewegung übrig bleiben, allerdings fehlen ja nun 17 Pixel.

Ich würde behaupten, dass die Mausbewegung an sich ja schon eine Integration über das TimeDelta des Frames darstellt und deswegen nicht mehr mit dt multipliziert werden darf.

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 12:50
von Schrompf
Hmm... eigentlich hast Du Recht. Die Mauskoordinaten bzw. deren Differenz ist ja bereits zeitskaliert. Wenn Du seltener abfragst, bekommst Du größere Differenzen.

Ich ging dort davon aus, dass das die typische "Pos = Pos + zeitdiff * Geschwindigkeit" Formel wäre. Allerdings war das nur unvollständig gelesen, sehe ich gerade. Ich weiß jetzt nicht genau, was Du da überhaupt vorhast. Wie soll die Steuerung sich denn verhalten?

Und ganz am Ende könnte Dein aktueller Fehler auch nur ein Messfehler sein: dass Du zwar ein sleep(20) eingebaut hast, aber Deine Zeitmessmethode diese 20ms nicht enthält, weil Du z.B. die Zeitmessung pro Frame schon vorher nimmst... schau da mal nach, das wär eine ebenso schlichte Vermutung.

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 13:27
von Zudomon
Leider funktioniert die Zeitmessung korrekt, weil ich darüber auch die FPS berechne. Und das kommt alles hin.

Es ist schwer das Bewegungsverhalten zu beschreiben. Also erstmal soll sie ziemlich genau die Mausbewegung übertragen. Zudem soll die Bewegung größer sein, wenn man die Maus schneller bewegt. Und das ganze soll noch irgendwie weich sein.

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 13:33
von eXile
De-factor ist doch sowas eigentlich nur ein iterativer Löser für eine Gleichung (häufig Differentialgleichung mit Anfangswertbedingen). Jeder Frame ist ein Iterationsschritt des Lösers. Dementsprechend müsste man auch Ansätze wie Runge-Kutta-Integration pro Frame durchführen können, und man müsste bessere Ergebnisse bekommen, als "einfach mit dem Zeitdelta zu multiplizieren".

Passend zum Thema: Erlaubt ist alles, solange nicht sowas bei rauskommt ;)

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 13:55
von Krishty
Zudomon hat geschrieben:Es ist schwer das Bewegungsverhalten zu beschreiben. Also erstmal soll sie ziemlich genau die Mausbewegung übertragen. Zudem soll die Bewegung größer sein, wenn man die Maus schneller bewegt. Und das ganze soll noch irgendwie weich sein.
„Mafia“ *schwärm* hatte so ein Mausverhalten. Damit konnte man hochpräzise zielen, sich aber dennoch sehr schnell umdrehen.

Wenn es nur um die Drehung geht, fallen mir zwei Methoden ein (die letzte würde ich bevorzugen):

a) Anhand der Framezeit und der Mausbewegung bestimmen, wieviel Grad (oder Bogenmaß, je nachem) pro Sekunde sich der Betrachter drehen möchte. Den Wert quadrieren. Dann werden kleine Mausbewegungen sehr klein, hastige Bewegungen sehr groß. Hat den Nachteil, dass die Bewegung abrupt aufhört, wenn auch der User aufhört die Maus zu bewegen.

b) Eine „virtuelle“ Drehung speichern, die exakt den unbearbeiteten Mauskoordinaten entspricht. Die „reale“ Drehung, die du anzeigst, ergibt sich zu jedem Zeitpunkt aus einer Interpolation zwischen der realen und der virtuellen Drehung. Benutzt du für die Interpolation kleine Werte, wird die virtuelle Drehung immer weiter von der angezeigten Drehung abweichen. Dadurch wird die reale Drehung sanft immer größer. Wenn die Maus still steht, nähert sich die angezeigte Drehung immer weiter der virtuellen an und die Drehung kommt dadurch sanft zum Stillstand. Die angezeigte Drehung dürfte dann exakt eine Bézierkurve mit der virtuellen Drehung als Kontrollpunkten sein, die Kamera verhält sich dann, als besäße sie Trägheit und würde durch den Cursor gezogen.

Ich werde letzteres gleich selbst mal implementieren, um zu gucken, wie es aussieht.

Gruß, Ky

Edit: Gerade implementiert. Bei einem Faktor von 0.2 ist es SO weich, dass es schon fast wehtut. Bei 0.5 ist alles großartig glatt.

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 16:27
von knivil
Aber wenn die FPS steigen, dann ist die Bewegung natürlich verfälscht.
Wie waere es denn erstmal mit einem FPS unabhaengigen Gameloop?

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 17:24
von Zudomon
knivil hat geschrieben:
Aber wenn die FPS steigen, dann ist die Bewegung natürlich verfälscht.
Wie waere es denn erstmal mit einem FPS unabhaengigen Gameloop?
Im Notfall wird mir da nichts anderes übrig bleiben. Sollte man ja für Physikberechnungen und dergleichen eigentlich auch machen.
Krishty hat geschrieben:Edit: Gerade implementiert. Bei einem Faktor von 0.2 ist es SO weich, dass es schon fast wehtut. Bei 0.5 ist alles großartig glatt.
Weich bekomme ich das ja auch, aber ebend nicht FPS unabhängig, da liegt das Hauptproblem.

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 17:54
von Krishty
Zudomon hat geschrieben:Weich bekomme ich das ja auch, aber ebend nicht FPS unabhängig, da liegt das Hauptproblem.

Code: Alles auswählen

// Die virtuelle Rotation deiner Kamera.
const float l_fVirtualXRotation = …;
const float l_fVirtualYRotation = …;

// In einer Sekunde soll sich die Kamera zu 99% an die virtuelle Position (die Zielposition) anpassen.
const float l_fCameraInertiaPerSecond = 1.0f - 0.99f;

// Füllst du mit der erwarteten Zeit dieses Frames, in Sekunden.
const float l_fFrameTimeInSeconds = …;

// Gibt an, wie stark sich die Kamera ergo innerhalb dieses Frames an die Zielposition anpasst.
const float l_fThisFramesCameraInertia = ::pow(l_fCameraInertiaPerSecond, l_fFrameTimeInSeconds);

// Jetzt bloß noch wie gehabt interpolieren …
const float l_fXRotation = (l_fVirtualXRotation * (1.0f - l_fThisFramesCameraInertia)) + (l_fXRotation * l_fThisFramesCameraInertia);
const float l_fYRotation = (l_fVirtualYRotation * (1.0f - l_fThisFramesCameraInertia)) + (l_fYRotation * l_fThisFramesCameraInertia);
Habe es mit 40 und vier fps getestet.

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 18:24
von Zudomon
Stimmt, so ist das weich und Zeitunabhängig! Super! Danke für den Code!

===== NACHTRAG =====
Wenn man das so für die Position nimmt, klappt das auch gut.
Das einzigste, was jetzt noch rein müsste, wäre, dass man sich schneller bewegt, wenn man schneller die Maus bewegt.

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 21:02
von Helmut
Wahrscheinlich willst du das jetzt nicht hören, aber ich würde solche Sachen nicht fps unabhängig programmieren. Zum einen ist es ziemlich anstrengend und aufwendig (manchmal sogar unmöglich) sowas so zu programmieren, zum anderen verlierst du so den Determinismus und erschwerst zB die Kollisionsberechnungen erheblich.
Eine feste FPS mit Frameskip hat eigentlich kaum Nachteile finde ich:)

Ciao

Re: Nichtlineare Bewegung

Verfasst: 27.03.2009, 23:53
von eXile
IMHO gibt es gerade bei Multiplayerspielen häufig eine Obergrenze (z.B. bei Doom 3 60 Hz). Das liegt daran, dass gerade wenn neben Physik und KI auch noch Netzwerk mit reinspielen, ganz komische Nebeneffekte auftreten können.

Re: Nichtlineare Bewegung

Verfasst: 28.03.2009, 13:01
von Krishty
Zudomon hat geschrieben:Das einzigste, was jetzt noch rein müsste, wäre, dass man sich schneller bewegt, wenn man schneller die Maus bewegt.
Das müsste aber selbst bei simpelsten Implementierungen der Mausbewegungen der Fall sein … wie berechnest du denn die Rotation/Position aus der Mausbewegung?

Re: Nichtlineare Bewegung

Verfasst: 28.03.2009, 13:09
von Zudomon
Krishty hat geschrieben:
Zudomon hat geschrieben:Das einzigste, was jetzt noch rein müsste, wäre, dass man sich schneller bewegt, wenn man schneller die Maus bewegt.
Das müsste aber selbst bei simpelsten Implementierungen der Mausbewegungen der Fall sein … wie berechnest du denn die Rotation/Position aus der Mausbewegung?
Klar, an sich ist das ja drin... also wenn man in einem Frame die Maus 10 Pixel bewegt, dann ist sind es auch nur 10 Einheiten, die bewegt werden... bei 20 Pixel ebend dann 20 Einheiten...
Was ich jetzt da noch gerne hätte wäre, das wenn man innerhalb von 0.1 Sekunden die Maus 10 Pixel bewegt, dass das z.B. dann nur halb so weit ist, wie wenn man die Maus 10 Pixel innerhalb von 0.05 Sekunden bewegt.

Vielleicht ist das alles übertrieben, das mal so exakt zu erörtern, aber ich würde es gerne mal "richtig" implementieren. ;)

Gruß
Zudomon

Re: Nichtlineare Bewegung

Verfasst: 28.03.2009, 13:20
von Krishty
Achso, jau, das ist dann das, was in den meisten Games als nichtlineare Bewegung tituliert wird … ganz einfach :)
Du berechnest aus der Mausbewegung und der aktuellen Frame-Zeit, um wieviel Einheiten (Grad oder Bogenmaß oder Meter) sich die Maus pro Sekunde bewegt … den Wert potenzierst du dann (Quadrat müsste reichen, aber kannst gerne experimentieren, interessiert mich auch) … multiplizierst dann wieder mit der Frame-Zeit und das ist dann die „neue“ Mausbewegung, mit der du im gewohnten Mechanismus weiter rechnen kannst.

Re: Nichtlineare Bewegung

Verfasst: 28.03.2009, 14:20
von Zudomon
Okay :D
Ich werde mich da gleich nochmal rannsetzen...
Krishty hat geschrieben:...als nichtlineare Bewegung tituliert wird.
Da bin ich ja froh, dass der Threadname dann doch nicht so falsch gewählt war!

Ich schätze mal, um die Mauswegung dann richtig zu erfassen, sollte man dann so einen fließenden Mittelwert ( oder wie das heißt ) einsetzen...

Re: Nichtlineare Bewegung

Verfasst: 28.03.2009, 14:29
von Krishty
Ja, wenn bei hohen Frameraten die Mausbewegung immer nurnoch entweder null oder ein Pixel groß ist, wird das Verhalten wieder linear … dann über mehrere Frames zählen. Bis 100fps sollte es aber gut funktionieren.

So extrem wie Helmut, dass man mit einer fixen Framerate arbeiten soll, sehe ich es zwar nicht – aber Bildwiederholraten über der Monitor-Aktualisierungsrate sind imho von Spezialanwendungen und Tests abgesehen Schwachsinn. Von daher würde ich den Mehraufwand für das Abdichten so hoher Framerates überhaupt nicht eingehen.

Re: Nichtlineare Bewegung

Verfasst: 28.03.2009, 15:45
von Zudomon
Irgendwie habe ich das Gefühl, ich bin zu dumm.
Also den gleitenden Mittelwert habe ich nun... gezählt wird das mittel der Differenz der Maus über 0.1 Sekunden. Nun das ganze zum Quadrat und diesen Wert mit der Mausverschiebung multiplizieren. Das funktioniert wunderbar... bei 1000 FPS... bei 100 ist wirkt die exponentiale Komponente viel stärker... wenn ich nun noch einfach das TimeDelta rein multipliziere bringt das auch nichts...

Bei der normalen Mausverschiebung darf man das Timedelta nicht mit rein multiplizieren, weil das durch die Art der Datenerfassung schon passiert ist. Wenn man nun den Mittelwert über 0.1 Sekunden berechnet, ist das ja auch schon eine entsprechende Integration und vom Gefühl her würde ich sagen, dass man da ebenfalls kein TimeDelta reinmultiplizieren darf.

Re: Nichtlineare Bewegung

Verfasst: 28.03.2009, 16:28
von Krishty
Du hast es also doch so implementiert … sind mehr als 100 fps denn wirklich soooo wichtig? Und du bist auch noch irgendwie total mit den Größen durcheinander gekommen …

Also, von Anfang an:

Du berechnest den gleitenden Mittelwert der Mausbewegung. Der Wert liegt dann – wie jede Mausbewegung – in Pixeln vor. (Floats benutzen!)

Durch die Division durch die Framezeit berechnest du die aktuelle Bewegungsgeschwindigkeit der Maus in Pixeln÷Sekunden.

Dann korrigierst du diesen Wert nach deinen Vorlieben (z.B. quadrieren).

Das multiplizierst du wiederum mit der Rotationsempfindlichkeit des Betrachters in Bogenmaß÷Pixel. Das Ergebnis ist die Rotation des Betrachters in Bogenmaß÷Sekunde.

Diesen Wert multiplizierst du mit der aktuellen Framezeit. Das Ergebnis (Bogenmaß) ist die Rotation, die du zu der des vorherigen Frames aufaddierst (bzw zur „virtuellen“ Rotation, wenn man unseren Glättungs-Mechanismus berücksichtigt).

Da wird nichts quadriertes mit der Mausbewegung multipliziert. Darum bin ich übrigens dafür, Dimensionsanalyse standardmäßig allen Programmiersprachen hinzuzufügen … ;)

Re: Nichtlineare Bewegung

Verfasst: 28.03.2009, 17:09
von Zudomon
Danke für die ausführliche Beschreibung. Ich will es noch nicht beschwören, aber ich glaube, jetzt funktioniert es.
Allerdings gibt es da noch ein paar Stolperfallen:

Der gemittelte Mittelwert, da darf man nicht durch die Anzahl der Samples teilen ( was mein Problem war ). Also speichert sich alle Datenwertepaare XY und den Zeitstempel, am besten in einem Ringarray. Dann durchläuft man dieses und summiert alle XY Paare, die kleiner als der gewünschte Zeitbereich ist.
Außerdem summiert man die Länge aller XY Paare, um diese "Beschleunigung" nicht Richtungsabhängig zu haben.

Der 2D Vektor der summierten Paare wird normalisiert, damit man eine gemittelte Richtung der Mausbewegung bekommt. Der Längenwert wird wie Krishty schon sagte,
erstmal durch die betrachtete Zeit geteilt, um die Änderung auf Sekunden um zu rechnen, und dann den Bedürfnissen angepasst... z.B. quadriert und skaliert.
Anschließend wird die Länge auf das TimeDelta des Frames runterskaliert und mit der gemittelten Richtung multipliziert.

Das Ergebnis dann auf die echte Rotation aufaddiert. Die virtuelle Rotation kann dann durch die vorgeschlagene weiche interpolation ermittelt werden.
Danke für eure Hilfe!! Ich probier das jetzt noch ein wenig aus, in der Hoffnung, diesmal endlich am Ziel zu sein. :D

Für alle, die sich fragen, warum denn diese exponentielle Bewegung: Ich will dadurch erreichen, dass man durch Rotation jeden Punkt mit der Maus ansteuern kann... was bei einer linearen Übertragung nicht Möglich ist.

Edit: Funktioniert nach ein paar weiteren Tests auf jeden Fall immernoch stabil. Bei 100, wie 1000 FPS alles super! Auch für die Postionierung der Kamera ist geeignet. Habe übrigens erstmal einen kleinen Wert von 1.25 als Exponent genommen. Cool ist auch das nun automatisch entstehende MotionBlur durch die flüssige Bewegung. :)