Why is WaitForSeconds causing my sprite to be changed?

79 views Asked by At

In my game, I play an animation, wait for the animation to end, and then I change the sprite image. Next I fade the sprite out, wait for the fade to end, then I disable the gameObject. Here's the code:

IEnumerator UnlockSequence()
{
    anim.Play("unlock");
    unlocking = false; // stops UnlockSequence from being called again
    yield return new WaitForSeconds(1.7f);

    sr.sprite = sprite1; //change sprite

    StartCoroutine(FadeTo(0.0f, 2.0f)); 
    yield return new WaitForSeconds(2f);

    gameObject.SetActive(false);
}

IEnumerator FadeTo(float aValue, float aTime)
{
    float alpha = sr.color.a;
    for (float t = 0.0f; t < 1.0f; t += Time.deltaTime / aTime)
    {
        Color newColor = new Color(sr.color.r, sr.color.g, sr.color.b, Mathf.Lerp(alpha, aValue, t));
        sr.color = newColor;
        yield return null;
    }
}

This works great, except when I get to yield return new WaitForSeconds(2f); the sprite image goes back to the one it was originally, before the animation. I have no clue why this happens. I know that sr.sprite = sprite1; works because when I take out yield return new WaitForSeconds(2f); the sprite is the correct one. Any help to fix this would be greatly appreciated.

EDIT:

Here is a gif of the problem, I'm sorry for it's terrible quality, but hopefully you can see the problem.

the problem

2

There are 2 answers

0
derHugo On BEST ANSWER

It is definitely not WaitForSeconds which changes the sprite but your Animator!

Reason

If somewhere in any of the AnimatorStates you have only one single keyframe for a certain property (like in your case apparently the Sprite) the Animator gains full control over this property and you can not overwrite it via a script anymore!

Further the AnimatorState has an option Write Defaults to which UnityTechnologies said in this thread

In 5.0 we have added a property on the State to Write back default values. Meaning that if turned on, it will write back the default values of all animated properties ( on a Controller wide-basis) that are not animated in that State.

By default, its "on", so we don't change the behavior that we had before.

So if you want the non-animated properties of a State to keep the value it had previously, simply turn it off.

What it means is that for every property that in this AnimatorState has no keyframes it uses the default value which the object would have if there was no Animator on it.

One Solution

So if you want your Default state to not overwrite the Sprite since there is no keyframe for the Sprite in that state then simply turn of this option.


Pure Animator Solution

However, I think you could setup your Animator in a way it could actually handle the entire thing without you needing your script except for starting the animation once.

You say your Animator setup looks like

Entry → Default ← Unlock

So allow me to ask:

  1. Why not simply remove that transition back to Default? This would make it stay in the unlock state until you tell it otherwise. By having the Loop Time turned of as you already have this means it stays at the last keyframe and wouldn't jump back to defaults anyway.

  2. Why not simply include the fading-out into your unlock animation itself as well? Simply set keyframes for the color property as well. This way you wouldn't need to use a script for that.

  3. Alternatively if you want to control Fading independently from the (un)lock you could use multiple Animation Layers! On the main layer you do your (un)locking. On a different layer you do the Fading via the Color property.

In general I would usually suggest an Animator setup using bool Animation Parameters for transitions. Then using multiple layers it could lok like

parameters 
   Bool "Open"
   Bool "FadeOut"

layers
    Base Layer

    Entry       -------------------> StateClosed
    StateClosed --(if Open==true)--> AnimOpening
    AnimOpening --(Exit time 1)----> StateOpen
    StateOpen   --(if Open==false)-> AnimClosing
    AnimClosing --(Exit time 1)----> StateClosed

    FadeingLayer (make sure to set Weight to `1` -  its `0` by default)

    Entry          -----------------------> StateFadedIn
    StateFadedIn   --(if FadeOut==true)---> AnimFadingOut
    AnimFaidingOut --(Exit time 1)--------> StateFadedOut
    StateFadedOut  --(if FadeOut==false)--> AnimFadingIn
    AnimFadingIn   --(Exit time 1)--------> StateFadedIn

If you want to go sure set keyframes for all according properties in all of the states including the single keyframe states. Meaning e.g. in the FadingLayer you set

StateFadedIn: Color.a = 1
StateFadedOut: Color.a = 0
AnimFadingOut: Color.a = well your keyframes here ;)
AnimFadingIn: Color.a = well your keyframes here ;)

and the BaseLayer the same way.

This way the Animator can not mess it up when force jumping between states via script.

And then in code rather simply set

anim.SetBool("Open", true);

and then

anim.SetBool("FadeOut", true);

Finally instead of the last script call you could enhance this even more setting up Animation Events that automatically invoke the FadeOut change when done. You could then still control it as well via script but wouldn't have to "manually" wait until the unlock animation is done before starting the fadeout.


I know this is a lot of information but I hope the idea gets clear and you can make your way through the documentation ;)

2
Mark Lorenz On

Hard to tell without seeing the animation however my theory would be that your Animation is looping back or causing the sprite to reset to its original sprite. The Animator replicates the properties of the GameObject based on the state they were in the timeline when the keyframes were created. I just replicated your code everything is working as expected (wait's 1.5 seconds, changes sprite, fades out after 2.0 seconds and go inactive). The only thing I didn't have is the animation clip you are using. When I have a bit of time later I can verify by adding an animation but I would look at that. If you could share a screenshot of the AnimationClip that would be helpful.