alicewithalex25
alicewithalex25

Reputation: 35

Wrong time inside Unity coroutine

I use coroutines for my animation work. And I must be sure that timings are perfect. For example I need to increase value overtime.I set time and stepCount to perform this. My code looks like this:

IEnumerator ScaleDown(float time)
{
    print(Time.time);
    float value = 0f;
    float deltaValue = 1 / (stepCount*time);
    float deltaTime = 1/stepCount;
    while (value < 1f)
    {
        value += deltaValue;
        meshRenderer.SetBlendShapeWeight(1, value * 100f);
        yield return new WaitForSeconds(deltaTime);
    }
    print(Time.time);
}

So when I set time=1f and stepCount=1 then real time between start and end print are 1 second. But when I increase stepCount to 100 or more keeping time=1, then real time is more then 1 second. Something about ~1.67f

So I have question: How can I use coroutines with specific stepCount and have perfect timing? I use stepCount for more smooth transition on blenshapes, shader variables like opacity. And must use stepCount>=100.

Upvotes: 0

Views: 1046

Answers (1)

derHugo
derHugo

Reputation: 90739

I use stepCount for more smooth transition on blenshapes, shader variables like opacity. And must use stepCount>=100.

These steps shall all happen in 1 second ... but:

Your code runs with a certain framerate optimal about 60 f/s (on a PC and within the editor mostly higher) which results in a "standard" Time.deltaTime of about 0.017 seconds between two frames!

yield return new WaitForSeconds(XY);

or in general anything using yield waits at least for one frame!

So of course your functionality breaks as soon as deltaTime becomes smaller then the actual possible framerate and real time between two frames. Especially for example when you say you want this to be executed >=100 times per second.

It also makes no sense to update a value more often then the framerate since no changes will be ever noted by the user faster then the frames are actually rendered.

Additionally SetBlendShapeWeight might be a performance intense call (don't know to be honest) so it might also slow down the execution causing additional lag.


How can I use coroutines with specific stepCount and have perfect timing

Beyond a certain limit you just can't.

But why do you need/want to use fixed steps for the value at all?

Since value is a float and what it seems you actually want is rather a smooth transition you could simply do

IEnumerator ScaleDown(float duration)
{
    print(Time.time);
    timePassed = 0f;
    do
    {
        // a linear interpolated value between 0 and 1
        var value = timePassed / duration;
        // optionaly you could even add some ease-in and ease-out
        //value = SmoothStep(0, 1, value);

        meshRenderer.SetBlendShapeWeight(1, value * 100f);

        // increase by the time passed since last frame
        // to avoid overshooting use Mathf.Min with the remaining difference
        // between duration and passedTime
        timePassed += Mathf.Min(duration - timePassed, Time.deltaTime);
        yield return null;
    } while (timePassed < duration);
    print(Time.time);
}

I you still want to fake it the other way round and make it update in fixed steps for stepCount < framerate you could simply round the smoothly transitioned values to that value like e.g.

value = Mathf.Round(value / deltaValue)) * deltaValue;

which should round up or down to the next full step of deltaValue.


Also note that in newer Unity versions the SetBlendShapeWeight is not automatically limited to the range [0; 100] anymore as it was in previous versions.

Though, most 3D modelling programs export models with the range [0;100] in Unity

The BlendShape weight range includes values between the minimum and the maximum weights defined in the model.

Upvotes: 0

Related Questions