前言:我們無法監聽Animator是否播放完一個影片,我所知的辦法就是將監聽方法設定為Public,并且掛在帶有Animator的物體上,并且還要在Clip檔案內新增AnimEvent,于是我自己寫了一個AnimCtrler,
需求:開始影片、暫停影片、將影片停止在某一個狀態、影片播放完成后執行回呼,
思路:
1.播放影片就用Animator自帶的API,Animator.Play即可;
2.停止影片,我想要Animator.speed為0達到效果;
3.停止在某一個狀態,我們可以先播影片,播放在第一幀時馬上暫停;
4.完成回呼,使用AnimatorStateInfo.normalizedTime是否>=1f判定
實作:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; [RequireComponent(typeof(Animator))] public class AnimCtrler : MonoBehaviour { private Animator m_animator; private float m_speed; private string m_curAnimName; private bool m_clipEndAutoSetNull; private Dictionary<string, Action<string>> clipEndCallbacks; public float Speed { get => m_speed; set { m_speed = value; if (m_animator != null) { m_animator.speed = m_speed; } } } public string CurAnimName { get => m_curAnimName; } /// <summary> /// 回呼執行后,是否會自動設定為空 /// </summary> public bool ClipEndAutoSetNull { set => m_clipEndAutoSetNull = value; } private void Awake() { m_animator = GetComponent<Animator>(); m_speed = 1f; m_animator.speed = m_speed; m_curAnimName = string.Empty; m_clipEndAutoSetNull = true; clipEndCallbacks = new Dictionary<string, Action<string>>(); } public void Play(string animName, float speed = 1) { if (string.IsNullOrEmpty(animName)) return; Speed = speed; m_animator.Play(animName, 0, 0); m_curAnimName = animName; } public void Stop() { Speed = 0f; } /// <summary> /// 將影片定格在這一瞬間 /// </summary> /// <param name="animName"></param> /// <param name="progress"></param> public void SetAnimStartPose(string animName, float progress) { if (string.IsNullOrEmpty(animName)) return; m_animator.Play(animName, 0, progress); m_animator.Update(0); Speed = 0f; } /// <summary> /// 設定影片結束的回呼 /// </summary> /// <param name="animName"></param> /// <param name="endCallback"></param> public void SetAnimEndCallback(string animName, Action<string> endCallback) { if (string.IsNullOrEmpty(animName)) return; if (clipEndCallbacks.ContainsKey(animName)) { clipEndCallbacks[animName] = endCallback; } else { clipEndCallbacks.Add(animName, endCallback); } } private void Update() { if (!string.IsNullOrEmpty(m_curAnimName)) { AnimatorStateInfo info = m_animator.GetCurrentAnimatorStateInfo(0); if (info.IsName(m_curAnimName) && info.normalizedTime >= 1f)//如果影片為loop模式,則此代碼會有bug { Action<string> callback = null; clipEndCallbacks.TryGetValue(m_curAnimName, out callback); callback?.Invoke(m_curAnimName); if (m_clipEndAutoSetNull) { clipEndCallbacks[m_curAnimName] = null; } } } } }
測驗:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Test : MonoBehaviour { public AnimCtrler ctrler; public void Update() { if (Input.GetMouseButtonDown(0)) { ctrler.ClipEndAutoSetNull = true; ctrler.SetAnimEndCallback("Move", (name) => Debug.Log(name)); ctrler.Play("Move"); } else if (Input.GetMouseButtonDown(1)) { ctrler.SetAnimStartPose("Exit", 1); } } }

總結:
在監聽影片完成條件這里會有一個bug,如果Clip的模式為loop,normalizedTime是會一直增長的,所以使用AnimCtrler腳本,影片片段都不應該是loop模式(靠normalizedTime判斷是否播放完,對這個問題是無解的,我在考慮是否用我以前寫的Timer來監聽),當然如果你使用Play重復播,監聽還是有效的,Animator的引數意義可以參考【Unity3D】Animation 和 Animator 影片重置到起始幀的方法_機靈鶴的博客-CSDN博客_animator.update
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/540034.html
標籤:其他
