Very simple animation playing... can't get it working in a neat and concise way.

Hello there.

I have a character that always keeps playing the ‘running’ animation but it also does play other anims (run/attack) and continue looping the running anim.

I have an object with an Animation (not Animator) component attached and the following script:

#pragma strict

var jumppower : int = 10;

var runspd : int = 10;

var runanim : AnimationClip;
var jumpanim : AnimationClip;
var attackanim : AnimationClip;

function Start () {

}

function AnimJump () {
	this.animation.clip = jumpanim;
	this.animation.Play();
	yield WaitForSeconds(this.animation.clip.length*2);
	this.animation.clip = runanim;
	this.animation.Play();

}

function Update () {

this.rigidbody2D.velocity.x = runspd/10.0f;

if (Input.GetKeyDown("c")) {
	this.rigidbody2D.AddForce(Vector2.up * jumppower);
	AnimJump();
}
else if (Input.GetKeyDown("v")) {
	
}

}

Note how the function “AnimJump” is a mess. (clip length is *2 because it’s a pingpong). I think that’s far from okay. I would like to not use clip names because the script is intended to run on various different situations and I don’t think using ‘clip.name’ is a good approach but it’s okay if there’s no other way… i mean, when you use a clip name the engine lookups for it using a string, and when you do ‘clip.name’ it also retrieves a string, so it’s just extra processing that’s not needed if you can just pass references to the very animation clip objects.

Anyway, animating is very easy and the thing works wonders, however, scripting is not straightforward imho and there’s a lack of docs.

Any help is appreciated.

Thank you in advance.

I’m fairly sure you have to use the Animation name for Legacy. A handy thing you can do is get all Animation Names though and look for something that is “Like” that name.

protected string GetAnimationNameFromList(string animationName)
	{
		foreach(AnimationState s in characterAnimation)
		{
			if(s.name.ToLower().Contains(animationName.ToLower()))
			{
				return s.name.ToString();
			}
		}
		return "";
	}

So if you had CharacterOne_Jump and another Character with CharacterTwo_Jump and you called GetAnimationNameFromList(“jump”) it would return either for you.

Looking at your code I noticed that your simply using the “Play()”. I would probably suggest you switch over to CrossFade so it smoothly attempts to transition from your jump into a run again.

The way we created our “AnimationController” was generic enough to work with all characters.
Example of one of the methods:

// This plays the Animation forward.
public float PlayAnimation(string animationName, float speed, string callBack = "", GameObject reciever = null, bool npc = false)
	{		
		// Play the animation
		characterAnimation[animationName].normalizedTime = 0f;
		characterAnimation[animationName].speed = speed;
		characterAnimation.CrossFade(animationName, 0.1f);				
		
		// Block the idle and run animation		
		if ((reciever != null)  !string.IsNullOrEmpty(callBack))
		{	// If the caller wants a message when the animation is finished
			StartCoroutine(AnimationCallBackTimer(characterAnimation[animationName].length/speed, callBack, reciever));
		}
		else
		{	
			StartCoroutine(AnimationTimer(characterAnimation[animationName].length/speed));
		}
		return characterAnimation[animationName].length;
	}	

//This will play the animation backwards.
public float PlayAnimationBackwards(string animationName, float speed, string callBack = "", GameObject reciever = null, bool npc = false)
	{
		characterAnimation[animationName].normalizedTime = 1f;
		characterAnimation[animationName].speed = speed;
		characterAnimation[animationName].time = characterAnimation[animationName].length;
		characterAnimation.CrossFade(animationName, 0.1f);
		
		if ((reciever != null)  !string.IsNullOrEmpty(callBack))
		{		
			StartCoroutine(AnimationCallBackTimer(characterAnimation[animationName].length/Mathf.Abs(speed), callBack, reciever));
		}
		else
		{
			StartCoroutine(AnimationTimer(characterAnimation[animationName].length/Mathf.Abs(speed)));
		}
		
		return characterAnimation[animationName].length;
	}

Instead of handling your Running and Idling… Just base that off of movement.
Example:

if(IsMoving())
			{
				currentAnimationSpeed = runAnimationSpeed;
				PlayAnimation(RunAnimation);
				idleAnimPlayed = false;				
			}
			else
			{
				//Not moving so were idle
				currentAnimationSpeed = idleAnimationSpeed;
				
				if (RandomIdleAnimStart  !idleAnimPlayed)
				{	// If we shall start at a random point of the animation (only for the firt time)
					characterAnimation[IdleAnimation].normalizedTime = UnityEngine.Random.Range(0.0f, 0.8f);
				}
				
				if (characterAnimation[IdleAnimation].normalizedTime >= 1.0f)
				{	// When the Idle animation is finished we call the event
					if (IdleAnimationFinished != null)
					{
						IdleAnimationFinished();
						characterAnimation[IdleAnimation].normalizedTime = 0.0f;
					}
				}
				
				PlayAnimation(IdleAnimation);
				idleAnimPlayed = true;		
			}

Simple IsMoving() checker for you

private bool IsMoving()
	{
		bool isMoving = false;
		
		//Calculate the movement velocity of the character.
		difference = myTrans.position - lastPos;
		lastPos = myTrans.position;
		playerVel = difference / Time.deltaTime;
		if(playerVel.sqrMagnitude > movementThreashhold)
		{
			isMoving = true;
		}
		return isMoving;
	}

Obviously don’t take my examples and throw them in Unity and think they will work with your Solution but you can get the jest of what is going on.

** And I’m sure your not wanting to jump and run or idle at the same time. So you can do a check before those checks to see if your in any other State. We use a boolean. So if your not in a “Movement State” => Casting an Ability. It won’t attempt to run or idle until its done.

if(conflictingAnimation)
			{
				return;
			}

Thank you very much. Honestly, it’s not the simple one-liner I was looking for :slight_smile: but your answer helped me a lot. I don’t like using the animation names, but that’s okay in this case imo. I’m going to take a look into the newer animator component however; perhaps it makes the life easier.

Thank you for your help.

Glad I could discourage :stuck_out_tongue_winking_eye:

I’ve tried using the new Mechanim and I’ve had a hard time with the Visual Editor and blending layers over animations. I probably just need to spend more time with it.

Haha. They really put some real efforts in making animation in Unity almost impossible to accomplish. I managed to workaround most problems, however, I want the ‘attack’ animation to always play immediately and stop all other animations, I couldn’t find a way to ‘reset’ (rewind doesn’t work) all the other animations though.

We play Attack animations every time the player asks to. Using the exact same methods from above.

https://dl.dropboxusercontent.com/u/32175794/_Examples/Eve_Revamp/2014_01_25-CloakSpin/CloakSpin.html

Still, I couldn’t figure a way to do what I want, and what I want is REALLY simple, however, I’m having problems with animations stuck (e.g. jump animation gets stuck when you attack) and animations not playing at all (e.g. attacking two times in a row very quickly will ignore the second attack).

I don’t need cross-fading, blending and stuff, just simply playing and sampling animations.

Here’s a pseudo-code:

IF ATTACK:
    IF jump animation is playing:
        sample frame 0 from jump animation
    play attack animation from start

ELIF JUMP:
    IF attack animation is NOT playing:
        play jump animation from start

ELSE:
    play running animation queued

Simple like that, I don’t need fancy effects and stuff, just need a way do exactly that without getting stuck animations and not playing animations at all. I could only do that by manually stepping the animations in my own function but that’s terrible.

Unity have the function for everything I need (playing, sampling and queuing animations) however, they doesn’t seem to work, for example, even when the attack animation is playing, setting the state time to 0 won’t play it from start, using Play(animstr) works but sometimes it will ignore the input when the anim is already playing, using both at the same time doesn’t work also.

It can’t be that hard…

public class AnimationController : MonoBehaviour 
{
    public enum AnimationState
    {
        jump, attack, none
    }

    private string jumpAnim = "Jump";
    private string attackAnim = "Attack";
    private string runAnim = "Run";
    private AnimationState curState = AnimationState.none;
    private AnimationState action = AnimationState.none;

    private Animation anim;
    public void Awake()
    {
        anim = GetComponent<Animation>();
    }
    
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            action = AnimationState.jump;
        }
        else if (Input.GetMouseButtonDown(0)) //Left mouse click
        {
            action = AnimationState.attack;
        }

        if (curState == AnimationState.none)
        {
            PreformAction();
        }
    }

    private void PreformAction()
    {
        switch (action)
        {
            case AnimationState.attack:
                //Were going to attack
                StartCoroutine(Attack());
                break;
            case AnimationState.jump:
                //Were going to jump
                StartCoroutine(Jump());
                break;
            case AnimationState.none:
                //Were going to run.
                Run();
                break;
        }
        action = AnimationState.none;
    }

    IEnumerator Jump()
    {
        curState = AnimationState.jump;
        //Play the animation forward.
        anim[jumpAnim].speed = Mathf.Abs(anim[jumpAnim].speed);
        anim.CrossFade(jumpAnim);
        yield return new WaitForSeconds(anim[jumpAnim].length);

        //Play it backwards? Kind of like a ping pong.
        anim[jumpAnim].normalizedTime = 1f;
        anim[attackAnim].speed = anim[jumpAnim].speed * (-1);
        anim.CrossFade(jumpAnim);

        yield return new WaitForSeconds(anim[jumpAnim].length);
        //Set our Current animation state to none.
        curState = AnimationState.none;
    }

    IEnumerator Attack()
    {
        curState = AnimationState.attack;
        anim.CrossFade(attackAnim);
        yield return new WaitForSeconds(anim[attackAnim].length);

        curState = AnimationState.none;
    }

    void Run()
    {
        if(!anim.IsPlaying(runAnim))
            anim.CrossFade(runAnim);
    }
}
  • Tested this myself. It transitions through all 3 animations fine.

Wow, thank you Ereous! I really appreciate your help!

That’s quite a lot of scripting for such a simple thing though, I was hoping for something like this: (port of my pseudocode above to JS)

if (attack) { // IF ATTACK:
	if(animation.IsPlaying("jump")){ // IF jump animation is playing:
		animation["jump"].time = 0; animation.Sample(); // sample frame 0 from jump animation
	}
	animation["attack"].time = 0; animation.Play("attack"); // play attack animation from start
}
else if (jump) { // ELIF JUMP:
	if (!animation.IsPlaying("attack")){ // IF attack animation is NOT playing:
		animation["jump"].time = 0;	animation.Play("jump"); // play jump animation from start
	}
}
else { // ELSE:
	this.animation.PlayQueued("run"); // play running animation queued
}

Having to deal with clip switching is too much hassle for such a simple thing imho, and having to deal with animation timing and stepping manually imho doesn’t really fit an engine so advanced like Unity… perhaps it’s because it’s a legacy feature and mecanim possibly is way better, I can’t stand video tutorials though, I feel retarded watching them :).

Best Regards

I’ve watched quite a few Mechanim videos. It seems like a pretty nice system. The only thing that gets me is the Layers. That script only took me a few minutes to write up. Probably because I’ve dealt with it so much.

I think in JS you can simple yield in any method. In c# you have to use the IEnumerator Methods.

Btw: I think your script would work fine. If you added in some Yielding like mine… To wait for the animation to finish.

Otherwise you will have overlapping animations. Also I think you want to use normalizedTime and possibly a check for the animation.Play(“run”) so it won’t effect itself constantly.

function HandleAnimations()
    {
        if (attack)
        { // IF ATTACK:
            if (animation.IsPlaying("jump"))
            { // IF jump animation is playing:
                animation["jump"].normalizedTime = 0; animation.Sample(); // sample frame 0 from jump animation
            }
            animation["attack"].normalizedTime = 0; animation.Play("attack"); // play attack animation from start
            //yield until the animation is done
        }
        else if (jump)
        { // ELIF JUMP:
            if (!animation.IsPlaying("attack"))
            { // IF attack animation is NOT playing:
                animation["jump"].normalizedTime = 0; animation.Play("jump"); // play jump animation from start
                //yield until the animation is done
            }
        }
        else
        { // ELSE:
            if (!animation.IsPlaying("run"))
                animation.Play("run"); // play running animation queued
        }

Thank you Ereous.

The problem with mechanim is that I don’t know how ‘efficient’ it would be… only to play simple animations it would be overkill imho, I could be wrong though.

Yeah, I don’t know why that script doesn’t work correctly and I’ve tried playing with it, inclusive adding ‘waiters’ ans stuff, but then more and more ‘stuck’ animations bugs in, then you have to deal with that by force playing the stuck things, but it’s not easy to detect that, so, in the end you have to add like 1000 lines of ‘workarounds’ in a simple script like that.

I think your approach is the best one… I mean, the best one would be if the animation stuff actually worked as expected so my simple script could just work correctly, but as that’s not the case, dealing manually with timings and stuff is the best way that works I think.

Also, about checking for the ‘run’ animation, I don’t think it’s necessary because I think “Play()” already checks if the animation is playing and doesn’t do anything in this case, otherwise it won’t be necessary to ‘reset’ animations to the start (using state.time=0) if you want to play it again from start while it’s already playing, and there’s no “pause” functionality, what’s another clue that it’s not necessary to check for the animation that’s already playing, however, I’m not sure though and also I don’t know if it won’t add an animation to the queue every frame (although your version isn’t using the PlayQueued()). I’m going to create another topic for that :).

My good sir, thank you so much i was pulling my hairs out trying to fix the unfinished animations but thsi worked like a charm, and i do love how you structered the code :wink: