Animations ignore TimeScale

Is it possible to play animations while the TimeScale is 0? I’m talking about bone animations imported from Maya.
The basic scenario is that i have a bunch of rigidbodies on screen, i’d like to “pause” the screen on a button press, which plays an animation and turns timeScale to 0. But because timeScale is 0 animations won’t play at all.

Is there any way of achieving this?

I’ve got another method where i use delegates and events to notify objects when i’ve entered “pause mode”, but i thought that just turning the timeScale down would be more efficient than N method calls.

Thanks

//edit//
One idea that i do have is that i could write an extension for the AnimationClip class for a new function to play the animation, but also pass in a flag (ignoreTimeScale), if this flag is true, then i could scrub through the animation using a while(animation.time < animation.length). Or something like that. Thoughts?

alt text

1 Like

Works perfectly fine by creating an animation extension function to play the animation based on realtimeSinceStartup and a psuedo deltaTime.

Add the following function to a script called “AnimationExtensions.cs”, and then you can simply call the function like so:

StartCoroutine( animation.Play(“animName”, false, () => Debug.Log(“onComplete”)) );

Play animation forward non timescale

    public static IEnumerator Play( this Animation animation, string clipName, bool useTimeScale, Action onComplete )
 {
 Debug.Log("Overwritten Play animation, useTimeScale? " + useTimeScale);
 //We Don't want to use timeScale, so we have to animate by frame..
 if(!useTimeScale)
 {
 Debug.Log("Started this animation! ( " + clipName + " ) ");
 AnimationState _currState = animation[clipName];
     bool isPlaying = true;
     float _startTime = 0F;
     float _progressTime = 0F;
     float _timeAtLastFrame = 0F;
     float _timeAtCurrentFrame = 0F;
     float deltaTime = 0F;
 
 
 animation.Play(clipName);
 
 _timeAtLastFrame = Time.realtimeSinceStartup;
         while (isPlaying) 
 {
             _timeAtCurrentFrame = Time.realtimeSinceStartup;
             deltaTime = _timeAtCurrentFrame - _timeAtLastFrame;
             _timeAtLastFrame = _timeAtCurrentFrame; 
 
                _progressTime += deltaTime;
                _currState.normalizedTime = _progressTime / _currState.length; 
                animation.Sample ();
 
 //Debug.Log(_progressTime);
 
                if (_progressTime >= _currState.length) 
 {
 //Debug.Log("Bam! Done animating");
 if(_currState.wrapMode != WrapMode.Loop)
 {
 //Debug.Log("Animation is not a loop anim, kill it.");
                     //_currState.enabled = false;
                     isPlaying = false;
 }
 else
 {
 //Debug.Log("Loop anim, continue.");
 _progressTime = 0.0f;
 }
                }
 
             yield return new WaitForEndOfFrame();
         }
         yield return null;
 if(onComplete != null)
 {
 Debug.Log("Start onComplete");
 onComplete();
 } 
 }
 else
 {
 animation.Play(clipName);
 }
 }

Play animation reverse non timescale
If you would like to do the same, but reverse your animations instead, you need to reverse your logic, like so:

 public static IEnumerator ReverseNonTimeScale( this Animation animation, AnimationClip _clip, Action onComplete )
 {
 AnimationState _currState = animation[_clip.name];
     bool isPlaying = true;
     float _progressTime = 0F;
     float _timeAtLastFrame = 0F;
     float _timeAtCurrentFrame = 0F;
     float deltaTime = 0F;
 
 animation.clip = _clip;
 
 Debug.Log(animation.clip.name);
 
 _timeAtLastFrame = Time.realtimeSinceStartup;
    while (isPlaying) 
 {
        _timeAtCurrentFrame = Time.realtimeSinceStartup;
        deltaTime = _timeAtCurrentFrame - _timeAtLastFrame;
        _timeAtLastFrame = _timeAtCurrentFrame; 

        _progressTime += deltaTime;
        animation.Play();
        _currState.normalizedTime = 1.0f - (_progressTime / _currState.length);
 //Debug.Log(_progressTime + ", " + _currState.normalizedTime);
        animation.Sample();
        animation.Stop();
 
        if (_progressTime >= _clip.length) 
 {
         _currState.normalizedTime = 0.0f;
 isPlaying = false;
        }
 
        yield return new WaitForEndOfFrame();
    }
    yield return null;
 if(onComplete != null)
 {
 Debug.Log("Start onComplete");
 onComplete();
 } 
 }

I pretty much did it the way you said in your edit. Base it off the timed components from the animation, and create a coroutine that I kept track of in my own List and based other functions that coincided with it off of Time.realtimeSinceStartup. This was just a helper timer to base coroutines off of since coroutine yielding is also derived form timeScale.

Just wanted to post what I ended up with taking snippets from this thread. doesn’t handle that much

using UnityEngine;

[RequireComponent(typeof(Animation))]
public class AnimationIgnoreTime : MonoBehaviour {

	private Animation animation;

	// Use this for initialization
	void Start() {
		animation = GetComponent<Animation>();
	}

	// Update is called once per frame
	void Update() {
		//Debug.Log(animation.isPlaying);
		if(animation.isPlaying == false) {
			return;
		}

		AnimationState currentState = animation[animation.clip.name];
		currentState.time += Time.unscaledDeltaTime;
		animation.Sample();
	}
}

Might I ask how someone goes about creating a
“creating an animation extension function”? Does AnimationExtensions have to be its own public static class? Or is there another method to do this? Thanks.

So I copied and pasted the c# script above and get these errors in the console. I never work with C# (only JS) and couldn’t work out what the problems are… can you help?

For the main script:
/AnimationExtensions.cs(1,28): error CS0116: A namespace can only contain types and namespace declarations

[is pointing to the following line in the script: public static IEnumerator Play( this Animation animation, string clipName, bool useTimeScale, Action onComplete ) ]

TYVM

I`m using this code for animations on pause:

using UnityEngine;

public class PlayAnimOnPause : MonoBehaviour
{
    Animator animator;
    int animHash;
    float curAnimTime;
    float totalAnimTime;

    void Start()
    {
        animator = GetComponent<Animator>();
        animHash = animator.GetCurrentAnimatorStateInfo(0).nameHash;
    }

    void Update()
    {
        if (totalAnimTime > 0)
        {
            curAnimTime += Time.unscaledDeltaTime;
            if (curAnimTime > totalAnimTime)
                curAnimTime -= totalAnimTime;

            animator.Play(animHash, 0, curAnimTime/totalAnimTime);
        }
        else
            totalAnimTime = animator.GetCurrentAnimatorStateInfo(0).length;
    }
}

Choose button/canvas/etc that you need → Animator → Update Mode → Unscaled Time

hope it will help anyone.

1 Like