Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Sa Jul 12, 2025 11:14

Foren-Übersicht » Programmierung » Allgemein
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 6 Beiträge ] 
Autor Nachricht
BeitragVerfasst: Mo Sep 07, 2009 01:38 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 04, 2008 23:15
Beiträge: 39
Wohnort: Oberösterreich
Programmiersprache: ObjPas, C, DivASM
Hallo,

Ich bin gerade dabei den Code meines Projektes aufzuräumen bzw. bestehendes zu erweitern, bevor ich den nächsten großen Abschnitt in Angriff nehme.
Bei der "Kamera" Klasse habe ich zb. bei den Kamerabewegungen eine Beschleunigung eingebaut. Dabei kam die Frage auf, wie ich später die Beschleunigung von Objekten, die sich zu einer bestimmten Position bewegen sollen oder von Animationen, implementieren soll.

Folgende Anforderungen habe ich mir gesetzt:
- Lineare Geschwindigkeitsinterpolation (Beschleunigung und Bremsbeschleunigung)
- Variable Beschleunigung und Maximalgeschwindigkeit.
- Völlig Zeit unabhängig (Position wird immer getroffen auch wenn weitaus mehr Zeit verstrichen ist als benötigt).

Diese Funktion ist dabei herausgekommen.
Code:
  1.  
  2. {
  3.   V0 = aktuelle Geschwindigkeit
  4.   S0 = Weg der noch zurückgelegt werden muss
  5.   A = Beschleunigung
  6.   Vmax = maximale Geschwindigkeit
  7.   Td = vergangene Zeit
  8. }
  9.  
  10. function InterpolateS(var V0, S0: Double; A, Vmax, Td: Double): Double;
  11. var
  12.   T, St, Sb, Sr, Vt: Double;
  13. begin
  14.   Result := 0;
  15.  
  16.   while Td > 0.00001 do
  17.   begin
  18.     Sr := 0;
  19.     Sb := (V0 * V0) / (A * 2);
  20.  
  21.     // Vmax begrenzen wenn diese nicht erreicht werden kann
  22.     St := (S0 - Sb) * 0.5 + Sb;
  23.     Vt := Sqrt(2 * St * A);
  24.     if Vt < Vmax then
  25.       Vmax := Vt;
  26.  
  27.     // deceleration
  28.     if ((Sb + 0.0001) > S0) then
  29.     begin
  30.       T := V0 / A;
  31.  
  32.       if (T < Td) or (S0 < 0.0001) then
  33.       begin
  34.       //  Sr := V0 * Td - 0.5 * (A * (Td * Td)); // nur zum Testen
  35.       //  V0 := V0 - A * T;
  36.       //  Log.Message('speed= ' + FloatToStr(V0) + ', sr= ' + FloatToStr(Sr));
  37.  
  38.         V0 := 0;
  39.         Sr := S0;
  40.       end else
  41.       begin
  42.         Sr := V0 * Td - 0.5 * (A * (Td * Td));
  43.         V0 := V0 - A * Td;
  44.       end;
  45.  
  46.       Td := 0;
  47.     end else
  48.  
  49.     // acceleration
  50.     if V0 < Vmax then
  51.     begin
  52.       T := (Vmax - V0) / A;
  53.  
  54.       if T > Td then
  55.       begin
  56.         Sr := 0.5 * (A * (Td * Td)) + V0 * Td;
  57.         V0 := A * Td + V0;
  58.         Td := 0;
  59.       end else
  60.       begin
  61.         Sr := 0.5 * (A * (T * T)) + V0 * T;
  62.         V0 := A * T + V0;
  63.         Td := Td - T;
  64.       end;
  65.  
  66.     end else
  67.  
  68.     // linear speed
  69.     begin
  70.       St := S0 - Sb;
  71.       T := St / V0;
  72.  
  73.       if T < Td then
  74.       begin
  75.         Sr := St;
  76.         Td := Td - T;
  77.       end else
  78.       begin
  79.         Sr := V0 * Td;
  80.         Td := 0;
  81.       end;
  82.     end;
  83.  
  84.     S0 := S0 - Sr;
  85.     Result := Result + Sr;
  86.   end;
  87. end;
  88.  


Nur bin ich mit dieser Funktion nicht wirklich zufrieden, den sie ist nicht sonderlich schnell und muss sogar noch erweitert werden, da Weg und Geschwindigkeit auch negativ sein können. Außerdem müsste diese Funktion bei einem jedem Objekt, das bewegt wird, für alle drei Achsen und die Rotation ausgeführt werden.

Ist das überhaupt der richtige Lösungsansatz oder wie wird dieses Problem zb. in Spielen gelöst?

humflo


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Sep 07, 2009 09:11 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 29, 2005 12:28
Beiträge: 2249
Wohnort: Düsseldorf
Programmiersprache: C++, C#, Java
Ich weiß nicht was du da berechnest (deine Variabelnamen sind dabei nicht gerade hilfreich), aber eigentlich muss man das Integral der Beschleunigung über die Zeit berechnen um ein physikalisch korrektes Ergebnis zu erhalten. Da Integrale nun mal nicht so leicht zu berechnen sind, verwendet man meistens eine Euler-Integration. Das heißt nichts weiter als das man die Beschleunigung für jeden Zeitschritt als konstant annimmt. Dann lässt sich die Geschwindigkeit v und die Position p eines Objektes in jedem Zeitschritt so berechnen:
Code:
  1. v = v + (timeElapsed / m) * j;
  2. p = p + timeElapsed * v;

Dabei ist j die Summe alle Kräfte und m die Masse des Objektes. Wenn du willst kannst du dann noch eine maximale Geschwindigkeit einbauen, wobei ich empfehle dies so zu implementieren:
Code:
  1. w = timeElapsed * c * v * v;
  2. if (|w| > |v|) { v = (0,0,0); }
  3. else           { v = v - w; }

Dabei ist c eine geeignete Konstante, z.B. 0.01. Bei eine Geschwindigkeit von 10 m/s würde dein Objekt so pro Sekunde um 1 m/s langsamer. Je schneller ein Objekt ist, desto schneller bremst es ab. Dadurch das v quadriert wird gibt es eine maximale Geschwindigkeit die aber nur schwer erreicht werden kann.

Zitat:
Außerdem müsste diese Funktion bei einem jedem Objekt, das bewegt wird, für alle drei Achsen und die Rotation ausgeführt werden.

In meinem obigen "Code" sind j,v und p natürlich 3D-Vektoren.

_________________
Yeah! :mrgreen:


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Sep 07, 2009 11:42 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2623
Wohnort: Berlin
Programmiersprache: Go, C/C++
Im Prinzip hat Coolcat das meiste schon genannt. Üblich sind ein Richtungsvektor, Positionsvektor und DeltaTime Variable.
Um In Kamera Richtung zu Bewegen wird einfach Position=Position+(Richtungsvektor*DeltaTime*Geschwindigkeit) gerechnet.
Um Seitwärts zur Richtung zu bewegen, also Strafe, berechnet man das Kreuzprodukt aus UpVektor(üblich x=0,y=1,z=0) und RichtungsVektor und der resultierende Vektor wird wieder mit Position=Position+(neue Vektor*DeltaTime*Geschwindigkeit) gerechnet.
Um entsprechende Gegenrichtungen zu erreichen kann man entweder noch ein negativen Richtungsvektor(Performancetechnisch besser) speichern oder einfach vor der Rechnung durch Vorzeichenwechsel errechnen und diesen Vektor statt Richtungsvektor zu nehmen.
Die Ausrichtung(nicht Richtungsvektor) wird oft in Quaternion oder als 3D Vektor auf sinus/cosinus Basis mit Winkel für die einzelnen Achsen erstellt. Sinus/Cosinus ist sehr Teuer, wenn man nicht gerade eine Lookuptable nutzt aber Quaternion sind recht teuer beim Umwandeln in die OpenGL geforderten RotationsMatrizen. Deswegen gibt es auch so unterschiedliche Kamerasysteme, es gibt viele Möglichkeiten für unterschiedliche Anforderungen und Komplexität.

Beschleunigung ist eine einzelne Variable, welche durch Multiplikation mit in die Formel einfließen kann, bzw. ein Vektor, wenn die Beschleunigungen auf den einzelnen Achsen unterschiedlich sein sollen. Diese Variable wird bei der Aktualisierung durch ein If konstruk unter Maximalgeschwindigkeit gehalten.

Wenn FPS drops oder peeks auftreten, dann wird die DeltaTime Variable stark verfälscht und deswegen entstehen dann falsche Positionen und so weiter.
Hierfür gibt es Kompensierungen indem, man z.B. eine Oberschranke einbaut, wird diese überschritten(zu wenig FPS), dann wird der Schrankenwert verwendet, welcher bei Physiksystemen in der Regel zwischen 25,27,30 und 60FPS(Minimale Frequenz für CRT Monitore und maximale für das Auge Wahrnehmbare Schranke für die meiste Bevölkerung) differiert. Also 1Sekunde / 25Frames=0,04..Sekunden Pro Frame, dies sind also eine DeltaTime von 40Milisekunden. Die 25Frames kommen vom PAL System, welches auf einer Bildwiederholfrequenz von 25Hz also 25FPS fest gelegt ist. Das rührt von Spielekonsolen her, welche ja auf PAL oder NTSC basieren und PAL ist der Standard mit der geringsten Bildwiederholfrequenz. Also steigt beim Update von DeltaTime der Wert über 40MSek, dann wird DeltaTime auf 40MSek gesetzt.

Es gibt noch Animationspfade, welche die Position und Richtung an einem bestimmten Zeitpunkt fest legt. Hierbei würde ich empfehlen einen Pfad für Position und einen für Direction zu speichern, wobei für jeden Punkt im Pfad der Keyframe angegeben wird, woraus dann per Linearer gleichung die Position für Zeit X gefunden werden kann.

Zum Thema optimierung, da geht soviel, da schreiben Menschen Bücher drüber, drum mal die meiner Meinung nach sinnvollsten Optimierungen.
Benutzt Float statt Double, denn die GPUs nutzen noch nicht lange Floats(vorher Half-Floats) und double ist erst noch in Aussicht. Die CPU ist ebenfalls nicht für Double optimiert, die FPU ist auf 4xFloat(sind 8 ALU Einheiten, jeweils eine für Mantisse und Exponent) und Double dauern knapp über die doppelte Zeit als float. Da weder GPU noch CPU Double nutzen macht es auch wenig Sinn. Erst bei Physikalischen Berechnungen, wo solch eine starke Genauigkeit notwenidig ist macht es wirklich Sinn. Ich kann mich dunkel erinnern, das bei Jedi ich mal eine SSE/MMX optimierte Vektorbibliothek gesehen habe, sonnst würde ich mal empfehlen eine zu suchen. Die Unterschiede sind von Compiler zu Compiler leider stark unterschiedlich, da z.B. einige sehr gute FPU optimierungen machen und dann der Geschwindigkeitszuwachs bei SSE/MMX Umstellung noch vieleicht Faktor 4-5 schneller ist wärend es auf anderen gut 20x schneller wird. Vektorbibliotheken machen dann auch dein Code übersichtlicher.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Sep 07, 2009 12:27 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 04, 2008 23:15
Beiträge: 39
Wohnort: Oberösterreich
Programmiersprache: ObjPas, C, DivASM
Erstmal danke für die ausführlichen Antworten.

Zitat:
Ich weiß nicht was du da berechnest...

Im Prinzip macht diese Funktion nichts anderes als den Weg "S0" zeit-basierend abzufahren. Es wird so weit beschleunigt wie es der aktuelle Weg oder die Maximalgeschwindigkeit zulässt, damit nach dem Abbremsen (mit negativer Beschleunigung) auf Geschwindigkeit = 0 der Weg auch 0 ist.
Quasi Geschwindigkeitsgenerator und Positionierung in einem. Wobei ich mittlerweile zum Entschluss gekommen bin das, dass nicht so schlau ist, und ich es trennen werde.

Zitat:
Hierfür gibt es Kompensierungen indem, man z.B. eine Oberschranke einbaut, wird diese überschritten(zu wenig FPS), dann wird der Schrankenwert verwendet, welcher bei Physiksystemen in der Regel zwischen 25,27,30 und 60FPS(Minimale Frequenz für CRT Monitore und maximale für das Auge Wahrnehmbare Schranke für die meiste Bevölkerung) differiert. Also 1Sekunde / 25Frames=0,04..Sekunden Pro Frame, dies sind also eine DeltaTime von 40Milisekunden.

Diese Funktion hat kein Problem damit. Egal wie hoch TimeDelta (Td) ist, es würde nur der vorgegebene Weg gefahren werden.

Werde jetzt erstmal das Ganze mit Richtungsvektoren, sowie Geschwindigkeitsgenerator und Positionierung getrennt umsetzten. Wobei eine Masse bei der Kamera zur Zeit noch keine Rolle spielt.

humflo


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Sep 07, 2009 13:58 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 29, 2005 12:28
Beiträge: 2249
Wohnort: Düsseldorf
Programmiersprache: C++, C#, Java
Zitat:
Im Prinzip macht diese Funktion nichts anderes als den Weg "S0" zeit-basierend abzufahren.

Fährst/Fliegst du vielleicht Wegpunkte oder so was ab? Ich verweise mal auf Splines.

_________________
Yeah! :mrgreen:


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Sep 09, 2009 18:01 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 04, 2008 23:15
Beiträge: 39
Wohnort: Oberösterreich
Programmiersprache: ObjPas, C, DivASM
Nein keine Splines. Werde ich aber bei Gelegenheit noch implementieren.

Ich hab mich wohl etwas unklar ausgedrückt. "S0" ist die Distanz zwischen der aktuellen Position und der Zielposition (Eindimensional).

Soweit ist alles klar. Danke.


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 6 Beiträge ] 
Foren-Übersicht » Programmierung » Allgemein


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 2 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.008s | 14 Queries | GZIP : On ]