Jump to content
Unity Insider Forum

Zeitliche Abläufe und wie halte ich sie konstant.


malzbie

Recommended Posts

Immer wieder kommen Fragen zu zeitlichen Abläufen auf.

Spielmechanismen die auf dem einen Rechner liefen, funktionieren auf dem anderen nicht.

Plötzlich springt ein Player viel weiter oder eine Rotation läuft viel zu langsam ab.

Warum das so sein kann will ich mal mit Beispielen erklären und auch die Lösungen dazu bringen.

 

Alles fängt damit an, dass man einen Körper über eine Eingabe vorwärts bewegen will.

 

Das Beispiel zeigt jetzt den reinen Code in einer Update Funktion. Es wird funktionieren, ist aber problematisch.

 

function Update(){
  if( Input.GetAxis ("Vertical"){
  rigidbody.AddRelativeForce(Vector3.forward*1000);
  }
}

 

Kurze Erklärung:

Die Update Funktion wird bei jedem Frame ausgeführt. Und dabei wird in unserem Beispiel jedes Mal abgefragt ob eine der vertikalen Steuerungstasten gedrückt wurde. Wenn ja, dann wird dem rigidbody eine Kraft zur relativen Vorwärtsrichtung hinzu gefügt. (also in die Richtung in die das Objekt über die Z Achse ausgerichtet ist)

 

Solang jetzt nichts anderes passiert und der Rechner seine ganze Kraft Unity geben kann, würde alles ganz konstant ablaufen. Wenn jetzt aber irgendetwas zusätzliches berechnet würde, oder die Grafikkarte wahnsinns Effekte darstellen soll würde sich die Framerate ändern. Sie würde geringer werden. Was passiert dabei mit unserer Steuerung?

 

Da die Abfrage ja nur einmal pro Frame durchlaufen wird, würde auch weiniger oft diese Vorwärtskraft ausgeübt werden. Vorher hatten wir vielleicht 200 fps (Frames per Second) und jetzt sind es mal angenommen nur noch 100 fps.

Wir üben also nur noch halb so oft die Kraft aus und unser Körper würde seine vorige Geschwindigkeit nicht mehr erreichen.

 

Wie können wir aber die Kraft konstant halten? Ganz einfach:

Wir fügen die deltaTime in unsere Formel ein.

Die deltaTime ist die Zeit, die zwischen 2 frames vergangen ist. Also von dem letzten Durchlauf der Funktion zum jetzigen Durchlauf.

 

function Update(){
  if( Input.GetAxis ("Vertical"){
  rigidbody.AddRelativeForce(Vector3.forward*10000*Time.deltaTime);
  }
}

 

Was passiert jetzt?

Die deltaTime ist ein Faktor, der eine geringere Framerate ausgleicht. War z.B. bei einer Framerate von 200 fps die deltaTime 0.1 groß so ist sie bei einer Framerate von 100 fps 0.2 groß. (Die Werte sind nur zur Verdeutlichung)

 

Vergeht also mehr Zeit zwischen den frames, wird einfach über die deltaTime die Kraft erhöht und somit wird jetzt in einer Sekunde immer die gleiche Kraft ausgeübt.

 

Das Ergebnis ist schon besser und es würde auch super funktionieren, wenn unser Körper nicht ein "Physikalisches" Objekt wäre. Jedes andere Objekt, welches ich im Raum bewegen oder drehen will, würde jetzt prima auf jedem Rechner ablaufen. Alles würde unabhängig von der Framerate einen gewissen weg zurück legen oder aber sich z.B. 2mal pro Sekunde drehen.

 

Nicht so bei einem rigidbody!

 

Um physikalische Berechnungen richtig ablaufen zu lassen, müssen wir eine andere Art Funktion nutzen.

Und zwar die FixedUpdate() Funktion.

 

Was mach die denn?

 

Grob gesagt, sie synchronisiert unsere physikalischen Berechnungen.

Eine FixedUpdate() Funktion wird unabhängig von der Framerate abgefragt. Sie wird über einen Zeitwert gesteuert, der in den Time Settings von Unity eingestellt und verändert werden kann.

Lasst die Werte aber so wie sie sind, solange ihr nicht wisst was ihr verursacht! ;)

 

Das FixedUpdate() wird also jetzt jedes mal ausgeführt, wenn eine gewisse Zeit überschritten ist.

Natürlich funktioniert das nur, wenn andere Berechnungen, ob von Unity oder dem Betriebssystem, es zu lassen.

Braucht die Grafikkarte recht lange um ein Frame darzustellen so kann es vorkommen, dass in der Zwischenzeit mehrere Male das FixedUpdate() durchlaufen wurde. In der Regel werden aber mehr frames gezeigt, als FixedUpdate Fuktionen durchlaufen werden.

Das sei nur am Rande gesagt, denn es ist kaum entscheidend.

 

Jedenfalls wird über die Zeitkonstante alles Physikalische berechnet also auch der Drag-Wert oder die Reibung auf Oberflächen und die Schwerkraft.

 

Bei unserem letzten Beispiel passiert ja alles in der Update() Funktion. Wir fügen bei jedem Frame also eine Kraft hinzu. Dummerweise werden alle anderen physikalischen Berechnungen aber über die Zeitkonstante berechnet.

Habe ich jetzt einen "langsamen" Rechner oder eine "langsame" Grafikkarte würde jetzt jedes Frame eine Kraft mit dem deltaTime Faktor hinzu gefügt werden, aber viel öfter würde die Reibung oder der Drag-Wert die Kraft wieder vermindern, als es bei einem schnellen Rechner passieren würde.

Schon kommt man nicht mehr auf die nötige Geschwindigkeit oder Höhe!

 

Deswegen alles Physikalische in die FixedUpdate() Funktion rein.

So sieht der richtige Code jetzt aus:

 

function FixedUpdate(){
  if( Input.GetAxis ("Vertical"){
  rigidbody.AddRelativeForce(Vector3.forward*10000*Time.deltaTime);
  }
}

 

Ihr denkt euch wahrscheinlich, dass man jetzt die deltaTime nicht mehr braucht. Doch man braucht sie noch, wenn auch nur noch minimal.

FixedUpdate wird zwar immer erst nach einer festgelegten Zeit ausgeführt, es kann aber durchaus sein, dass die Berechnungen zwischendurch zu lange dauern und deswegen kann diese Zeit sich auch erhöhen. (viel Physik, viele Scripts)

 

Hier findet man die Info zu den Time Einstellungen: http://unity3d.com/s...imeManager.html

 

Fazit:

Um Bewegungen auf unterschiedlich schnellen Rechnern gleich ablaufen zu lassen bzw. um Schwankungen der Framerate auszugleichen, die deltaTime als Faktor für die Berechnungen nutzen.

Immer wenn die Physik im Spiel ist ein FixedUpdate() nutzen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

@ Sascha:

Ja das hoffe ich. :)

 

@jonichecker:

Die fixedDeltaTime ist eine Konstante bzw. Variable, die ich beschreiben kann.

Das ist eigentlich der Wert vom fixedTimestep im Time Manager.

Man kann diese Variable innerhalb eine Funktion nutzen, um z.B. etwas erst dann auszuführen, wenn auch die FixedUpdate() durchlaufen würde. Das würde aber nur Sinn machen, wenn mehr frames ablaufen, als fixed Intervalle.

Ich sehe da eigentlich keine Anwendungsmöglichkeit da ich ja gleich alles im FixedUpdate() machen kann.

 

Was aber wichtig ist: Man muss diese Variable auch anpassen, wenn man an der Timescale rumgespielt hat.

Denn wenn ich die Timescale auf 0.5 , also halbe Geschwindigkeit, bringe, dann muss ich auch die fixedDeltaTime dementsprechend anpassen. Denn über diesen Wert werden meine Physikalischen berechnungen UND die FixedUpdate() Funktion ausgeführt.

Das machste so:

Time.fixedDeltaTime = 0.02 * Time.timeScale;

Link zu diesem Kommentar
Auf anderen Seiten teilen

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Lädt...
×
×
  • Neu erstellen...