Jump to content
Unity Insider Forum

beim Start Variablen im Zeitraum x ändern (Code vereinfachen)


KaBau

Recommended Posts

Hi,

ich habe häufig die Anforderung, dass am Anfang (beim Start oder beim Aktivieren des Script), sich der Wert einer Variable in einem definierten Zeitraum vom Wert "A" zu Wert "B" ändern soll. Im beigefügten, funktionierndem Beispiel soll die Drehgeschwindigkeit "backRotSpeed" von 0.0f auf 0.3f innerhalb von 5 Sekunden ansteigen:

public class EnyBBackRotY : MonoBehaviour
{
    float backRotY;
    [SerializeField]
    float backRotSpeed = 0.3f;
    float realBackRotSpeed = 0.0f;
    float slowBackRotEnd;
    float slowBackRotSlowDown;
    [SerializeField]
    float slowRotDuration = 5.0f;
    [SerializeField]
    Transform player;
    [SerializeField]
    float between = 1.0f;
    bool slowRotYes = true;

    private void Start()
    {
        backRotY = transform.eulerAngles.y;
        slowBackRotEnd = Time.time + slowRotDuration;
        slowBackRotSlowDown = backRotSpeed / slowRotDuration / 50; // durch 50 für die Durchläufe in der Fixupdate Methode

    }

    private void Update()
    {
        
        if (player.transform.position.x > between)
        {
            backRotY += 1 * realBackRotSpeed * (player.transform.position.x - between);
        }

        if (player.transform.position.x < -between)
        {
            backRotY += 1 * realBackRotSpeed * (player.transform.position.x + between);
        }
    }

    private void FixedUpdate()
    {
        if (slowRotYes && Time.time <= slowBackRotEnd)
        {
            realBackRotSpeed += slowBackRotSlowDown;
        }

        transform.eulerAngles = new Vector3(0, -backRotY, 0);
    }
}

Nur muss ist nun zum einem in der Start Methode zwei Zeilen eintragen und noch einmal in der FixedUpdate Methode. Somit habe ich für eine Sache (welche nur kurz benötigt wird) an zwei Stellen Eintragungen, was der Lesbarkeit des Code nicht zugute kommt. Gefühlt würde ich sagen, dass ich mit einer Coroutine arbeiten muss. Aber irgendwie fehlt mit hier noch der Ansatz, wie ich sagen kann, dass der Wert der Variable innerhalb der Coroutine von A nach B wechselt und das in der vorgegebenen Zeitvorgabe. Wie geschrieben: es funktioniert, sollte aber sicher übersichtlicher möglich sein.

Link zu diesem Kommentar
Auf anderen Seiten teilen

habs jetzt nicht getestet, aber ich meine du kannst auch einfach math.lerp verwenden:
 

public class myScript : MonoBehaviour {

    public float speed = 1f;

    float backRotSpeed = 0.3f;
    float realBackRotSpeed = 0.0f;


    private void FixedUpdate()
    {
        while (realBackRotSpeed < backRotSpeed)
        {
            realBackRotSpeed = Mathf.Lerp(realBackRotSpeed, backRotSpeed, speed * Time.deltaTime);
        }
    }
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Am 31.3.2019 um 17:58 schrieb KaBau:

Gefühlt würde ich sagen, dass ich mit einer Coroutine arbeiten muss

Sehr gute Idee!

Coroutinen funktioneren so: Du hast eine Methode, die ganz normal abläuft mit dem kleinen Unterschied, dass du eine Anweisung zur Hand hast, mit der du eine beliebige Anzahl an Frames warten kannst. Du kannst damit also dasselbe machen wie mit Update, aber eben als zusammenhängender Ablauf.

Wenn du z.B. diesen Code hast:

private int frameCounter = 0;

private void Update()
{
  if (frameCounter < 60)
  {
    frameCounter++;
    if (frameCounter == 60)
    {
      Debug.Log("Ich habe 60 Frames abgewartet.");
    }
  }
}

dann hast du genau die Probleme, die du bereits angemerkt hast. Deine Zählervariable ist irgendwo abseits des Codes, der damit arbeitet, und darüber hinaus läuft da eine Update-Methode, die die meiste Zeit über gar nichts sinnvolles macht.

So sieht das Äquivalent in einer Coroutine aus:

private IEnumerator CountToSixtyFrames()
{
  for (var frameCounter = 0; frameCounter < 60; frameCounter++)
  {
    yield return null; // warte einen Frame
  }
  Debug.Log("Ich habe 60 Frames abgewartet.");
}

Es gibt hier nur zwei Dinge zu bemerken:

  1. Die Methode hat als Rückgabetyp "IEnumerator".
  2. Mit "yield return null" wird die Methode unterbrochen und im nächsten Schritt des Update-Zyklus an dieser stelle fortgesetzt.
    Statt null kann man auch andere Dinge zurück geben, Z.B. ein WaitForSeconds-Objekt, dann wird nicht einen Frame lang gewartet, sondern so viele Frames, dass die angegebene Zeit in Sekunden verstrichen ist.

Gestartet wird eine Coroutine so:

StartCoroutine(CountToSixtyFrames());

In deinem Fall kannst du also etwa so etwas machen:

private IEnumerator AnimateRotationSpeed(float targetValue, float duration)
{
  var startValue = rotationSpeed;
  for (var time = 0f; time < duration; time += Time.deltaTime)
  {
    var progress = time / duration;
    rotationSpeed = Mathf.Lerp(startValue, targetValue, progress);
    yield return null;
  }
  rotationSpeed = targetValue;
}

und dann in Start aufrufen:

private void Start()
{
  StartCoroutine(AnimateRotationSpeed(targetRotationSpeed, 1f));
}

Kann man natürlich auch noch etwas modularer machen, statt fest rotationSpeed zu beeinflussen einfach einen ref-Parameter oder ein Callback anbieten und schon hat man sein eigenes Tweening-System :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo @Sascha und @Jomnitech,

danke für die Tipps. Ich denke mir, dass der Tipp von Jomnitech auch funktioniert, nur schmirt mit in letzter Zeit bei einer "while" Schleife immer mein Unity (Version 2018.3.0f2) ab. Das hatte ich schon an anderer Stelle gehabt.

Sascha: beim Aufrufen der Coroutine meinte Visual Studio, dass eine Klammer am Ende fehlt:

StartCoroutine(AnimateRotationSpeed(backRotSpeed, slowRotDuration));

Die habe ich nachgetragen. Zudem hat Visual Studio bei "time += Time.deltaTime" gemeckert, weil der Typ "float" nicht in "int" konvertiert werden kann. Somit habe ich diese Zeile wie folgt abgeändert:

for (var time = 0.0f; time < duration; time += Time.deltaTime)

Der Unterschied ist im vordersten "time = 0.0f".

Klappt wunderbar. Ich spare mir einige Variablen am Anfang (ist somit übersichtlicher), und die "Zeitschleife" ist aus der FixedUpdate Methode raus. Sollte der Code noch erweitert werden bleibt das somit übersichtlicher. Danke.

So, und mit "Mathf.Lerp" muss ich mich nun dringend doch einmal auseinander setzen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...