Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Play animation sequence

Discussion in 'Editor & General Support' started by kagura, Feb 19, 2011.

  1. kagura

    kagura

    Joined:
    Jan 3, 2011
    Posts:
    54
    Hi,
    I have 3 animation A,B,C.
    I want to play animation A then B then C. When animation C is completed, I will show a notice.
    How can I do that? I don't know how to callback when animation complete .

    Thanks so much.
     
    StankAwake likes this.
  2. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,551
    Use CrossFadeQueued() so the animations will play one after the other. To check if an animation has finished playing, store the AnimationState in a temporary variable:

    Note: I am using C#

    Add this as a member variable to your class:
    Code (csharp):
    1. AnimationState anim;
    Put this in Start() (change nameOfAnimation to the actual animation name):
    Code (csharp):
    1. anim = animation["nameOfAnimation"];
    Then put this in Update():
    Code (csharp):
    1.  
    2. if (anim.normalizedTime == 1.0f)
    3. {
    4.   // animation is finished. do something here
    5. }
    6.  
    I have not tested this code so tell me if there's anything wrong.
     
  3. kagura

    kagura

    Joined:
    Jan 3, 2011
    Posts:
    54
    It is not work. Because normalize time is checked before animation complete.
     
  4. cemC

    cemC

    Joined:
    Dec 23, 2010
    Posts:
    214
    You can use animation.Crossfade( ) function.
     
  5. kagura

    kagura

    Joined:
    Jan 3, 2011
    Posts:
    54
    Code (csharp):
    1. void Update()
    2. {
    3.      animation.CrossFade("attack");
    4.    
    5.      if (anim.normalizedTime == 1.0f)
    6.      {
    7.           //  do something..
    8.      }
    9.  
    10. }
    The code in do something is not work
     
  6. kagura

    kagura

    Joined:
    Jan 3, 2011
    Posts:
    54
    Please help me
     
  7. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,551
    Again, I said use CrossFadeQueued, not CrossFade, so that they will be played one after the other.

    Code (csharp):
    1.  
    2. AnimationState anim;
    3.  
    4. void Start()
    5. {
    6.   animation.CrossFadeQueued("animation1");
    7.   animation.CrossFadeQueued("animation2");
    8.   animation.CrossFadeQueued("animation3");
    9.   anim = animation["animation3"];
    10. }
    11.  
    12. void Update()
    13. {
    14.   if (anim.normalizedTime == 1.0f)
    15.   {
    16.     Debug.Log("animation3 is finished");
    17.   }
    18. }
    19.  
    nomalizedTime starts at 0.0 at the beginning, 0.5 at the middle of the animation, and will become 1.0 when the animation is finished.
     
    RickshawDerpyDerp likes this.
  8. kagura

    kagura

    Joined:
    Jan 3, 2011
    Posts:
    54
    Thanks.
    I want to play animation in a function , not on Start()
    Example when I click a button, animaton sequence start, then show a notice when complete
     
    RickshawDerpyDerp likes this.
  9. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,551
    Then put the code that plays the animation in a different function then call that function when you need it to.
     
  10. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
  11. tranos

    tranos

    Joined:
    Feb 19, 2014
    Posts:
    180
  12. udit014patel

    udit014patel

    Joined:
    Dec 9, 2019
    Posts:
    3
    I also have 3 animations but I want to play each animation on mouse click in sequence and pop-up a panel with some instructions. I am very new to unity. How can I do that?
     
    RickshawDerpyDerp likes this.
  13. RickshawDerpyDerp

    RickshawDerpyDerp

    Joined:
    Nov 6, 2018
    Posts:
    25
    @udit014patel Sounds like you want to do exactly the thing I am trying to do!
     
    udit014patel likes this.
  14. udit014patel

    udit014patel

    Joined:
    Dec 9, 2019
    Posts:
    3
    can you share how you did this. It might be helpful to me.
     
  15. RickshawDerpyDerp

    RickshawDerpyDerp

    Joined:
    Nov 6, 2018
    Posts:
    25
    Hi, @udit014patel, I ended up using Unity's state machine transition parameters (which belong to the animator component) and set the animation parameters in FixedUpdate(). I even got the timed hits working!


    This might not make a lot of sense but I will post my code from the script where all the animations are handled:


    Code (CSharp):
    1. // LuigiAnimationEvents.cs
    2.  
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6. using UnityEngine.SceneManagement;
    7. using UnityEngine.UI;
    8. using TMPro;
    9. //using UnityEditor.Animations;
    10.  
    11. public enum ControlState { PLATFORMING, BATTLE }
    12.  
    13. public class LuigiAnimEvents : MonoBehaviour
    14. {
    15.     public ControlState ctrlState;
    16.     Animator animator;
    17.  
    18.     // BattleMenu
    19.     public GameObject battleMenu;
    20.     public Button jumpButton;
    21.     public Button fireballButton;
    22.     public Button attackButton;
    23.     public Button defendButton;
    24.     public Button itemButton;
    25.     // Anim Controllers
    26.     public RuntimeAnimatorController platformingController;
    27.     public RuntimeAnimatorController battleController;
    28.     // Targets
    29.     public Transform playerSpawnPoint;
    30.     public Transform enemySpawnPoint;
    31.     public List<Transform> meleeTargets;
    32.     public List<Transform> rangedTargets;
    33.     public List<Transform> jumpTargets;
    34.     // party
    35.     private GameObject luigiPrefab;
    36.     // flags, targets, etc.
    37.     public GameObject signal; // make visible when timed hit is active, for debug purposes
    38.     private bool doTimedHit;
    39.     private bool failedTimedHit;
    40.     private Transform target;
    41.     float lerpTime;
    42.  
    43.     void Start()
    44.     {
    45.         Application.targetFrameRate = 60;
    46.         SetupControllerState();
    47.  
    48.         if (ctrlState == ControlState.BATTLE)
    49.         {
    50.             InitializePrivates();
    51.             GetBattleMenu();
    52.             if (battleMenu != null) GetButtons();
    53.             else Debug.LogError("ERROR: LuigiAnimEvents::battleMenu is empty. No buttons, no battle menu.");
    54.          
    55.             // Add listeners to buttons:
    56.             attackButton.onClick.AddListener(call: delegate { PhysicalAttack(); });
    57.         }
    58.     }
    59.  
    60.     void InitializePrivates()
    61.     {
    62.         // set target
    63.         if (meleeTargets.Count != 0) target = meleeTargets[0];
    64.         else if (rangedTargets.Count != 0) target = rangedTargets[0];
    65.         else
    66.         {
    67.             target = enemySpawnPoint;
    68.             Debug.LogError("ERROR: target initialized to \'enemySpawnPoint\'.This should not happen. There is no enemy target in any of the Target lists.");
    69.         }
    70.         lerpTime = 0f;
    71.         doTimedHit = false;
    72.         failedTimedHit = false;
    73.         luigiPrefab = GameObject.FindGameObjectWithTag("Player"); // there is only one party member right now
    74.         signal = GameObject.Instantiate(signal, new Vector3(-2f,1.5f,7f), Quaternion.identity);
    75.         signal.SetActive(false);
    76.     }
    77.  
    78.     void FixedUpdate()
    79.     {
    80.         if (animator.GetBool("boolRunToTarget") == true)
    81.         {
    82.             LerpOverTime(playerSpawnPoint.position, target.position, 0.5f);
    83.             if (lerpTime >= 0.5f)
    84.             {
    85.                 animator.SetBool("boolRunToTarget", false);
    86.                 lerpTime = 0f;
    87.             }
    88.         }
    89.  
    90.         if (animator.GetCurrentAnimatorStateInfo(0).IsName("Run_Back_Home")) //yes, I want him to run backwards, its funny
    91.         {
    92.             LerpOverTime(target.position, playerSpawnPoint.position, 0.5f);
    93.             if (lerpTime >= 0.5f)
    94.             {
    95.                 animator.SetBool("boolRunBackHome", false);
    96.                 lerpTime = 0f;
    97.             }
    98.         }
    99.  
    100.         // reset flags when action sequence is over
    101.         if ((failedTimedHit == true || doTimedHit == true) && animator.GetCurrentAnimatorStateInfo(0).IsName("Battle_Idle"))
    102.         {
    103.             failedTimedHit = false;
    104.             doTimedHit = false;
    105.         }
    106.     }
    107.  
    108.     void Update()
    109.     {
    110.         //+----------------------------------------------------------------------------+
    111.         //|                               PHYSICAL ATTACK                              |
    112.         //+----------------------------------------------------------------------------+
    113.         if (animator.GetCurrentAnimatorStateInfo(0).IsName("First_Punch"))
    114.         {
    115.             float t = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
    116.             // handle player's timed hit input (if there is any)
    117.             if ((t >= 29f / 83f && t < 57f / 83f) && Input.GetButtonDown("Jump") && failedTimedHit == false)
    118.             {
    119.                 Debug.Log("triggerTimedHit");
    120.                 doTimedHit = true;
    121.             }
    122.  
    123.             // prevents player from succeeding by spamming buttons:
    124.             else if (t < 29f/83f && Input.GetButtonDown("Jump"))
    125.                 failedTimedHit = true;
    126.  
    127.             if (t >= 41f / 83f && doTimedHit == true)
    128.             {
    129.                 animator.SetBool("boolTimedHit", true);
    130.             }
    131.  
    132.             // deal with timed-hit signal: (all signal debug lines work)
    133.             if (t >= 29f / 83f && t < 57f / 83f && signal.activeInHierarchy == false) signal.SetActive(true);
    134.             if (t >= 57f / 83f && signal.activeInHierarchy == true) signal.SetActive(false);
    135.         }
    136.  
    137.         else if (signal.activeInHierarchy == true) signal.SetActive(false); // in case we are already out of punch animation
    138.     }
    139.  
    140.     private void LerpOverTime(Vector3 start, Vector3 end, float duration)
    141.     {
    142.         if (lerpTime <= duration)
    143.         {
    144.             lerpTime += Time.deltaTime;
    145.             float percent = Mathf.Clamp01(lerpTime / duration);
    146.             transform.position = Vector3.Lerp(start, end, percent);
    147.         }
    148.     }
    149.  
    150.     //+------------------------------------------------------------------------------+
    151.     //|                          BUTTON EVENTS / "ACTIONS"                           |
    152.     //+------------------------------------------------------------------------------+
    153.  
    154.     // PHYSICAL ATTACK
    155.     public UnityEngine.Events.UnityAction PhysicalAttack()
    156.     {
    157.         battleMenu.SetActive(false);
    158.         if (meleeTargets.Count == 1) target = meleeTargets[0];
    159.         else target.position = Vector3.zero;
    160.         AnimTrigger("triggerPunch");
    161.         animator.SetBool("boolRunToTarget", true);
    162.         return null;
    163.     }
    164.  
    165.     //+------------------------------------------------------------------------------+
    166.     //|                                    SETUP                                     |
    167.     //+------------------------------------------------------------------------------+
    168.  
    169.     void SetupControllerState()
    170.     {
    171.         if (SceneManager.GetActiveScene().buildIndex == 1)
    172.         {
    173.             ctrlState = ControlState.PLATFORMING;
    174.             this.GetComponent<Animator>().runtimeAnimatorController = platformingController as RuntimeAnimatorController;
    175.             this.GetComponent<CharacterController>().enabled = true;
    176.             this.GetComponent<PlayerMovement>().enabled = true;
    177.         }
    178.         else if (SceneManager.GetActiveScene().buildIndex == 2)
    179.         {
    180.             ctrlState = ControlState.BATTLE;
    181.             this.GetComponent<Animator>().runtimeAnimatorController = battleController as RuntimeAnimatorController;
    182.             this.GetComponent<CharacterController>().enabled = false;
    183.             this.GetComponent<PlayerMovement>().enabled = false;
    184.             // initialize targets:
    185.             GameObject[] targets = GameObject.FindGameObjectsWithTag("Target");
    186.             foreach (GameObject obj in targets)
    187.             {
    188.                 if (obj.name == "MeleeTarget") meleeTargets.Add(obj.transform);
    189.                 else if (obj.name == "RangedTarget") rangedTargets.Add(obj.transform);
    190.                 else if (obj.name == "JumpTarget") jumpTargets.Add(obj.transform);
    191.                 else if (obj.name == "SpawnPlayer") playerSpawnPoint = obj.transform;
    192.                 else if (obj.name == "SpawnEnemy") enemySpawnPoint = obj.transform;
    193.             }
    194.         }
    195.         animator = GetComponent<Animator>();
    196.     }
    197.  
    198.     void GetBattleMenu()
    199.     {
    200.         GameObject[] taggedItems = GameObject.FindGameObjectsWithTag("BattleMenu");
    201.         if (taggedItems.Length != 0)
    202.         {
    203.             foreach (GameObject obj in taggedItems)
    204.             {
    205.                 if (obj.name == "BattleMenu")
    206.                 {
    207.                     battleMenu = obj;
    208.                     return;
    209.                 }
    210.             }
    211.         }
    212.  
    213.         Debug.LogError("ERROR: BattleMenu not found!");
    214.     }
    215.  
    216.     void GetButtons()
    217.     {
    218.         jumpButton = battleMenu.transform.GetChild(0).GetChild(2).GetChild(0).GetChild(0).GetComponent<Button>();
    219.         fireballButton = battleMenu.transform.GetChild(0).GetChild(2).GetChild(0).GetChild(1).GetComponent<Button>();
    220.         attackButton = battleMenu.transform.GetChild(1).GetChild(2).GetChild(0).GetChild(0).GetComponent<Button>();
    221.         defendButton = battleMenu.transform.GetChild(2).GetChild(2).GetChild(0).GetChild(0).GetComponent<Button>();
    222.         if (battleMenu.transform.GetChild(3).GetChild(2).GetChild(0).childCount == 0)
    223.         {
    224.             itemButton = null; //takes care of case where there are no items
    225.         }
    226.         else
    227.             battleMenu.transform.GetChild(3).GetChild(2).GetChild(0).GetChild(0).GetComponent<Button>();
    228.     }
    229.  
    230.     void TestButtons()
    231.     {
    232.         Debug.Log(jumpButton.GetComponentInChildren<TMP_Text>().text);
    233.         Debug.Log(fireballButton.GetComponentInChildren<TMP_Text>().text);
    234.         Debug.Log(attackButton.GetComponentInChildren<TMP_Text>().text);
    235.         Debug.Log(defendButton.GetComponentInChildren<TMP_Text>().text);
    236.         if (itemButton != null) Debug.Log(itemButton.GetComponentInChildren<TMP_Text>().text);
    237.         else Debug.LogError("ERROR: LuigiAnimationEvents::itemButton = null. No item means no item button.");
    238.     }
    239.  
    240.     // this is not being used yet, used to make sure all triggers get reset when a new one is activated.
    241.     void AnimTrigger(string triggerName)
    242.     {
    243.         foreach (AnimatorControllerParameter p in animator.parameters)
    244.             if (p.type == AnimatorControllerParameterType.Trigger)
    245.                 animator.ResetTrigger(p.name);
    246.         animator.SetTrigger(triggerName);
    247.     }
    248. }
    There's a little bit here that isn't relevant to you, but I don't want to pick through it right now. I will say that the "signal" object is just that red box in the GIF that appears whenever the timed-hit window opens. It is coded separately from the part that reads user input so if the user is spamming buttons before the window opens and gets "locked-out" of the timed-hit window, the box appears anyway.

    I do the checks for user input in Update() so there are more checks between the limited animation frames and the user has a better chance of their input being read. Think about it: If the animations run on FixedUpdate and I set the framerate to 60fps, then nothing in that loop is getting processed faster than once every 60th of a second. That sounds fast (and it is) but if the user does a button input during the FIRST or LAST possible moment, I don't want it getting ignored because it happened in the first 0.9/60th of a second or the last 0.9/60ths of a second--get it? More checks will be done in Update() because Update() runs as quickly as the computer's processor can handle.

    In order to manually set the framerate, you have to turn off Vsync either in the script or in the Player preferences (the Edit-->Preferences-->Player)

    You can ask me more questions on my discord server:
    TheDisorganization#5589
    Good luck!
     
    Last edited: Jan 3, 2020