Chain Attacks (Animations really) together

In my PlayerInput file, I have the following in the Update function

        if(Input.GetButtonDown("Attack"))
        {
            SendMessage("MeleeAttack");
        }

This kicks off the process ok and calls MeleeAttack, which is turn calls PlayAttackSequence. But, I am having an issue, when I click more than once, it kind of starts the first animation and then restarts it and the plays the sequence. My desired behavior is if I 1. press the mouse once, play animation one
2. press the mouse twice, play animation one, then animation two
3. press the mouse three times, play animation one, then animation two, then animation three

The mouse clicks need to happen consecutively. If the user user clicks once and then waits five seconds and then clicks twice, the loop should start over. Hopefully this is clear and someone can help. I would certainly appreciate it!

/// <summary>
/// AdvancedMovement.cs
/// Dec 27, 2010
/// Peter Laliberte
/// 
/// This script is responsible for getting the players movement inputs and adjusting the characters animations accordindly.
/// 
/// This script will be automatically attached to your player or mob with the use of the PlayerInput.cs and AI.cs scripts.
/// 
/// This script assumes that you have these animation with the following names:
	/// Player:
	/// swim	- the swim forward animation (only needed if you want to swim in game)
	/// walk	- a walk animation
	/// run		- a run animations
	/// side	- for strafing
	/// jump	- a jump animation
	/// fall	- a falling animation
	/// idle	- an idle animation
	/// 
	/// Mob:
	/// run		- a run animations
	/// jump	- a jump animation
	/// fall	- a falling animation
	/// idle	- an idle animation
/// 
/// </summary>
using UnityEngine;
using System.Collections;

[RequireComponent (typeof(CharacterController))]
public class AdvancedMovement : MonoBehaviour {
	public enum State {
		Idle,
		Init,
		Setup,
		Run
	}
	
	public enum Turn {
		left = -1,
		none = 0,
		right = 1
	}
	public enum Forward {
		back = -1,
		none = 0,
		forward = 1
	}
	
	public enum Flight {
		down = -1,
		none = 0,
		up = 1
	}

    //10.3.12
    public enum MeleAttack
    {
        sword_swing_high_right,
        sword_swing_high_left,
        sword_swing_high_straight_down
    }
	
    //S.Murley 10.3.12 - using temp animation
	public string walkAnimName = "sword_walk";
	public string runAnimName;
	public string jumpAnimName;
	public string idleAnimName;
	public string swimForwardAnimName;
	public string strafeLeftAnimName;
	public string strafeRightAnimName;
	public string fallAnimName;

    //S.Murley 10.2.12
    public string[] arrMeleAttacks = new string[]{"sword_swing_high_right", "sword_swing_high_left", "sword_swing_high_straight_down"};
    public float mouseClicks = 0;
	public int attackSequence;
    private float mouseTimer = 0f;
    private float mouseTimerLimit = 1.25f;
    private float lastMouseClick = 0;
    private float timer;
    //public AnimationClip meleeAttack1;
    //public AnimationClip meleeAttack2;
    //public AnimationClip meleeAttack3;
    public GameObject characterObject;
    //private int attackSequence = 0;

	public AnimationClip idleIncombat1h;
	
	
	public float walkSpeed = 2;					//the speed our character walks at
	public float runMultiplier = 2;				//how fast the player runs compared to the walk speed
	public float strafeSpeed = 2.5f;			//the speed our character strafes at
	public float rotateSpeed = 250;				//the speed our character turns at
	public float gravity = 20;					//the setting for gravity
	public float airTime = 0;					//how long have we been in the air since the last time we touched the ground
	public float fallTime = .5f;				//the length of time we have to be falling before the system knows its a fall
	public float jumpHeight = 8;				//the height we move when we are jumping
	public float jumpTime = 1.5f;
	public float attackWeight= 1.5f;
	
	
	private CollisionFlags _collisionFlags;		//the collisionFlags we have from the last frame.
	private Vector3 _moveDirection;				//This is the direction our character is moving
	private Transform _myTransform;				//our cached transform
	private CharacterController _controller;	//our cached CharacterController
	
	private Turn _turn;
	private Forward _forward;
	private Turn _strafe;
	private Flight _flight;
	private bool _run;
	private bool _jump;
	private bool _isSwimming;
	public bool _isFlying;

	
	private State _state;
	
	private BaseCharacter _bc;
	
	//Called before the script starts
	public void Awake() {
		_myTransform = transform;								//cache our transform
		_controller = GetComponent<CharacterController>();		//cache our charactercontroller
		
		_state = AdvancedMovement.State.Init;
		
		_bc = gameObject.GetComponent<BaseCharacter>();
	}

	// Use this for initialization
	IEnumerator Start () {
		while(true) {
			switch(_state) {
			case State.Init:
				Init();
				break;
			case State.Setup:
				SetUp();
				break;
			case State.Run:
				ActionPicker();
				break;
			}

			yield return null;
		}
	}

    void Update()
    {
        
        //reset clicks after 1 second if mouse clicks > 0
        if (Time.time - lastMouseClick > 1.0F  mouseClicks > 0)
        {
            resetClicks();
        }
    }
	
    private void Init() {
		if(!GetComponent<CharacterController>()) return;
		if(!GetComponent<Animation>()) return;
		
		_state = AdvancedMovement.State.Setup;
	}
	
	private void SetUp() {

		_moveDirection = Vector3.zero;							//zero our the vector3 we will use for moving our player
		animation.Stop();										//stop any animations that might be set to play automatically
		animation.wrapMode = WrapMode.Loop;						//set all animations to loop by default
		
//		if(jumpAnimName != "") {
//			animation[jumpAnimName].layer = -1;						//move jump to a higher layer
//			animation[jumpAnimName].wrapMode = WrapMode.Once;		//set jump to only play once
//		}

		animation.Play(idleAnimName);							//start the idle animation when the script starts
		
		
		_turn = AdvancedMovement.Turn.none;
		_forward = AdvancedMovement.Forward.none;
		_strafe = Turn.none;
		_flight = Flight.none;
		_run = true;
		_jump = false;
		_isSwimming = false;
		_isFlying = false;
		
		_state = AdvancedMovement.State.Run;
	}
	
	private void ActionPicker() {
		//allow the player to turn left and right
		_myTransform.Rotate(0, (int)_turn * Time.deltaTime * rotateSpeed, 0);

		
		//if we are on the ground, let us move
		if(_isFlying){			
			_moveDirection = new Vector3((int)_strafe, (int)_flight, (int)_forward);
			_moveDirection = _myTransform.TransformDirection(_moveDirection).normalized;
			_moveDirection *= walkSpeed;
			//	FlyingUpdate();	
		}
		else if(_controller.isGrounded || _isSwimming) {
		//if(_controller.isGrounded || _isSwimming) {
			//reset the air timer if we are on the ground
			airTime = 0;
			
			//get the user input if we should be moving forward or sideways
			//we will calculate a new vector3 for where the player needs to be
			_moveDirection = new Vector3((int)_strafe, 0, (int)_forward);
			_moveDirection = _myTransform.TransformDirection(_moveDirection).normalized;
			_moveDirection *= walkSpeed;
			
			if(_forward != Forward.none) {						//if user is pressing forward
				if(_isSwimming) {
					Swim();
				}
				else {
					if(_run) {									//and pressing the run key
						_moveDirection *= runMultiplier;		//move user at run speed
						Run();									//play run animation
					}
					else {
						Walk();									//play walk animation
					}
				}
			}
			else if(_strafe != AdvancedMovement.Turn.none) {	//if user is pressing strafe
				if(_strafe == Turn.left)
					StrafeLeft();										//play strafe animation
				else
					StrafeRight();										//play strafe animation
			}
			else {
				if(_isSwimming) {
				}
				else {
					Idle();										//play idle animation
				}
			}
			
			if(_jump) {										//if the user presses the jump key
				if(airTime < jumpTime) {					//if we have not already been in the air to long
					_moveDirection.y += jumpHeight;			//move them upwards
					Jump();									//play jump animation
					_jump = false;
				}
			}
			
		}
		else {
			//if we have a collisionFlag and it is CollideBelow
			if((_collisionFlags  CollisionFlags.CollidedBelow) == 0) {
				airTime += Time.deltaTime;					//increase the airTime
				
				if(airTime > fallTime) {					//if we have been in the air to long
					Fall();									//play the fall animation
				}
			}
		}
		
		if(!_isSwimming || !_isFlying)
			_moveDirection.y -= gravity * Time.deltaTime;		//apply gravity
		
		//move the character and store any new CollisionFlags we get
		_collisionFlags = _controller.Move(_moveDirection * Time.deltaTime);
	}
   private float moveSpeed;
	//public float rotateSpeed = 100.0f;
    public float speedSmoothing = 10.0f;
	
    public float flyingSpeed = 15.0f;
    public float flyingBackSpeed = -5.0f;
   
    public float runFlyMultiplier = 3.1f;
    public float elevateMultipler = 4.0f;
   
    private bool isFlying = false;
    private bool isFlyingBackward = false;	
    void FlyingUpdate()
    {

        // ----------------Movement Controls      ---------------------//

        //sets target direction by making a vector with the keys that you have pressed.
        // the 1.0f is the vertical value. If you try to turn with only h being pressed, it will act funny
        Vector3 targetDirection = /*Input.GetAxis("Horizontal") * transform.right + */ 0.3f * _myTransform.forward;
       targetDirection = targetDirection.normalized;

       //creates a vector that will smoothly rotate toward destination. Normalized for good measure    
        _moveDirection = Vector3.Lerp(_moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime);
                //moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
        _moveDirection = _moveDirection.normalized;

        //Does the final rotation along as there is being input pressed (targetDirection)
        /*
        if (targetDirection != Vector3.zero)
        {
            transform.rotation = Quaternion.LookRotation(moveDirection);


        }
         * */

        //--Find out the movement
        float v = Input.GetAxisRaw("Vertical");
        float h = Input.GetAxisRaw("Horizontal");

        //Vector3 targetDirection2 = v * transform.forward;
        //Vector3 targetDirection2 = h * transform.right + v * transform.forward;

        // Choose target speed
        //* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
        //float targetDirection3 = Mathf.Min(targetDirection2.magnitude, 1.0f);;
                float targetSpeed = 20f;

//        if (Mathf.Abs(v) < 0.1f  Mathf.Abs(h) < 0.1f )
//        {
//            targetSpeed = 0;
//            isFlyingBackward = false;
//        }
//        else if (v < -0.1f)
//        {
//            targetSpeed = flyingBackSpeed;
//            isFlyingBackward = true;
//        }
//        else
//        {
            targetSpeed = flyingSpeed;
//            isFlyingBackward = false;
//        }
                   
        //sets the current smooth speed
        // Smooth the speed based on the current target direction
        float curSmooth = speedSmoothing * Time.deltaTime;


        //sets the movement after all the scenarios have been calculated
        moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
                //playerManager.MoveSpeed(moveSpeed);
        //Debug.Log("Move Speed:" + moveSpeed + " Target Speed:" + targetSpeed + " CurSmoth:" + curSmooth );

        // Calculate actual motion
        Vector3 movement = _moveDirection * moveSpeed;
        movement *= Time.deltaTime;

               

       //collisionFlags = controller.Move(movement);
            // ----------------FLying rotation Controls      ---------------------//
     
        //for mouse movement
        float yawMouse = Input.GetAxis("Mouse X");
        float pitchMouse = _moveDirection.y;//Input.GetAxis("Mouse Y");        
        Vector3 targetFlyRotation = Vector3.zero;

        Screen.lockCursor = true;
       
        if (Mathf.Abs(yawMouse) > 0.1f || Mathf.Abs(pitchMouse) > 0.1f)
        {
            targetFlyRotation = yawMouse * _myTransform.right + pitchMouse * _myTransform.up;
            targetFlyRotation.Normalize();
            targetFlyRotation *= Time.deltaTime * 3.0f;

           
            //limit x rotation if looking too much up or down
            //Log out the limitX value for this to make sense
            float limitX = Quaternion.LookRotation(_moveDirection + targetFlyRotation).eulerAngles.x;


                //70 sets the rotation limit in the down direction
                //290 sets limit for up direction
            if (limitX < 90  limitX > 70 || limitX > 270  limitX < 290)
            {
                //Debug.Log("restrict motion");
            }
            else
            {
                _moveDirection += targetFlyRotation;
               //does the actual rotation on the object if no limits are breached
                _myTransform.rotation = Quaternion.LookRotation(_moveDirection);
                               
                               
            }

            //just tells the playerManager where to Look for the aim target.
//                        Vector3 newLookDirection = lookObject.transform.position -  transform.position;
//                        newLookDirection.Normalize();
//               
//                        playerManager.LookDirection(newLookDirection);
         

        }
    }

    

    //S.Murley 10.2.12
    public void resetClicks()
    {
        mouseClicks = 0;
        mouseTimer = 0;
        lastMouseClick = 0;
    }

    //S.Murley 10.2.12
	
	void OnGUI() 
	{
		Event e = Event.current;
	    if (e.isMouse)
	    	Debug.Log("Mouse clicks: " + mouseClicks++);
    }	
	
    public void MeleeAttack()
    {

        //attackSequence = 0;

        //if mouse click equal 0, and first click, increment to 1
        if (mouseClicks == 0)
        {
            mouseClicks += 1;
        }
        else if (Time.time - lastMouseClick < mouseTimerLimit)
        {
            mouseClicks += 1;
        }

        //loop through current click number and set sequence
        if (mouseClicks >= 1  mouseClicks < 4)
        {
            mouseTimer += Time.fixedDeltaTime;

            if (mouseClicks == 3)
            {
                if (mouseTimer - mouseTimerLimit < 0)
                {
                    attackSequence = 3;
                    //Debug.Log("Mouse Triple Click");
                }
                else
                {
                    attackSequence = 2;
                    //Debug.Log("Timer expired on Triple, do Double");
                    resetClicks();
                }
            }
            else if (mouseClicks == 2)
            {
                if (mouseTimer - mouseTimerLimit < 0)
                {
                    attackSequence = 2;
                    //Debug.Log("Mouse Double Click");
                }
                else
                {
                    attackSequence = 1;
                    //Debug.Log("Timer expired on Double, do Single");
                    resetClicks();
                }
            }
            else if (mouseClicks == 1)
            {
                if (mouseTimer - mouseTimerLimit < 0)
                {
                    attackSequence = 1;
                    //Debug.Log("Mouse Single Click");
                }
                else
                {
                    //Debug.Log("Timer expired on Single, reset clicks");
                    resetClicks();
                }

            }
            else if (mouseTimer > mouseTimerLimit)
            {
                //Debug.Log("Timer expired");
                resetClicks();
            }
        }
        else
        {
            resetClicks();
        }

        lastMouseClick = Time.time;

        PlayAttackSequence(attackSequence);
    }

	public void MoveMeForward(Forward z) {
		_forward = z;
	}
	
	public void ToggleRun() {
		_run = !_run;
	}
	
	public void RotateMe(Turn y) {
		_turn = y;
	}
	
	public void Strafe(Turn x) {
		_strafe = x;
	}
	
	public void JumpUp() {
		_jump = true;
	}
	
	public void IsSwimming(bool swim) {
		_isSwimming = swim;
	}
	
	public void SetFlyHeight(Flight x) {
		_flight = x;
	
	}	
	
	public void ToggleFly() {
		_isFlying = !_isFlying;
	}		
	
	
/**
 * Below is a list of all of the animations that every character in the game can perform along with any parameters needed for them to work right
**/
	//Idle animation
	public void Idle() {
		if(idleAnimName == "")
			return;
		
		if( !_bc.InCombat )
			animation.CrossFade(idleAnimName);
		//else
		//	Debug.LogWarning( "Attack Idle" );
	}
	
	//walk animation
	public void Walk() {
		if(walkAnimName == "")
			return;
		
		//animation.CrossFade(walkAnimName);
        //Debug.Log(walkAnimName);

        animation[arrMeleAttacks[0]].wrapMode = WrapMode.Once;
        animation[walkAnimName].wrapMode = WrapMode.Loop;
        animation[walkAnimName].weight = .5f;	

        animation.CrossFade(walkAnimName);
        animation[arrMeleAttacks[0]].layer = 1;
        animation.CrossFade(arrMeleAttacks[0]);
        animation[arrMeleAttacks[0]].weight = .5f;		
	}
	
	//run animation
	public void Run() {
		if(runAnimName == "")
			return;

		animation[runAnimName].speed = 1.5f;
		animation.CrossFade(runAnimName);
	}
	
	//strafe left animation
	public void StrafeLeft() {
		if(strafeLeftAnimName == "")
			return;

		animation.CrossFade(strafeLeftAnimName);
	}
	
	//strafe right animation
	public void StrafeRight() {
		if(strafeRightAnimName == "")
			return;

		animation.CrossFade(strafeRightAnimName);
	}	
	
	//jump animation
	public void Jump() {
		if(jumpAnimName == "")
			return;
		animation.CrossFade(jumpAnimName);
	}
	
	//fall animation
	public void Fall() {
		if(fallAnimName == "")
			return;

		animation.CrossFade(fallAnimName);
	}
	
	public void Swim() {
		if(swimForwardAnimName == "")
			return;

		animation.CrossFade(swimForwardAnimName);
	}

    //S.Murley 10.2.12
    public void PlayAttackSequence(int attackLevel)
    {
        //Debug.Log(attackLevel);

        _bc.InCombat = true;

        ////S.Murley 10.2.12
        characterObject = GameObject.Find("Thor");

        for (int attackCntr = 0; attackCntr < attackLevel; attackCntr++)
        {
            characterObject.animation[arrMeleAttacks[attackCntr]].wrapMode = WrapMode.Once;

            if (attackCntr == 0)
            {
                characterObject.animation.PlayQueued(arrMeleAttacks[attackCntr], QueueMode.PlayNow);
            }
            else if (attackCntr <= attackLevel - 1)
            {
                characterObject.animation.PlayQueued(arrMeleAttacks[attackCntr], QueueMode.CompleteOthers);
            }
        }

        //yield return new WaitForSeconds(attackLevel);

        //yield return new WaitForSeconds(characterObject.animation.clip.length);
    }

}

I’m not clear on this, but you’re basically attempting to que just animations, or are you attempting to que a whole line of code?

If you just need the animation bit, you can use Animation.PlayQueued

However, if this is an attack animation that has associated code for doing other things like creating a hit volume, casting damage, or anything else, you’ll want to check if the animation in question is playing before executing your attack function. You can do this by checking the enabled property of the animation in question like so:

var attackAnimation : AnimationClip; // This variable contains the animation clip

function Attack () {
	if (animation[attackAnimation.name].enabled == true) {//checks if the attack animation is playing
	//Code which plays your animation and does all other attack related stuff
	}
}

In this case you can just call Attack() on your user’s click, and it won’t actually do anything unless the attack animation is not playing. This prevents the user from interrupting an attack in progress with a new attack.

In the case of chaining 1,2,3 attacks just use an int and set it to the proper value each time you fire an animation. So when you fire anim 1, set it to 2, firing 2 sets it to 3, firing 3 sets it to 1 again, etc.

For the “cooldown” you can just timestamp every attack with Time.deltaTime, filling a float with the current time each time the player attacks, then just check it against Time.deltaTime + 5

I struggled with this problem for a while aswell as i didn’t find much about this topic and found this solution from MWMusker pretty useful.
I added Tags to the animation states and make use of the isTag() for reusability aswell as no timer is needed.

public void OnAttack()
{
int attackComboNr = playerAnimations.GetCurrentAttackAnimationNr() + 1;
playerAnimations.SetAttackComboNr(attackComboNr);
}

public int GetCurrentAttackAnimationNr()
{
if (Animator.GetCurrentAnimatorStateInfo(0).IsTag(“Attack0”))
return 1;
if (Animator.GetCurrentAnimatorStateInfo(0).IsTag(“Attack1”))
return 2;
if (Animator.GetCurrentAnimatorStateInfo(0).IsTag(“Attack2”))
return 3;
if (Animator.GetCurrentAnimatorStateInfo(0).IsTag(“Attack3”))
return 4;
else
return 0;
}