主要代码逻辑是创建一个action队列,当动画播放结束时就移除队头,执行后面的事件
public class Enemy : MonoBehaviour
{
public event Action E_AnimatorFin;//当动画播放完毕时
public Action DefaultAction;//默认事件
public Dictionary<Action, string> EventAnimator= new();//Event对应的动画
public List<Action> Eventlist=new();//要做的event列表
private Animator ani;
public virtual void Start()
{
ani = GetComponent<Animator>();
E_AnimatorFin += OnAnimatorFinish;
StartCoroutine(SpawningAnimator());
IEnumerator SpawningAnimator()
{
yield return new WaitForSeconds(0.02f);
PlayAnimator(ani, EventAnimator[ Eventlist[0]],
() =>
{
Debug.Log("动画播放前执行代码"+ EventAnimator[ Eventlist[0]]);
},
() =>
{
E_AnimatorFin?.Invoke();
Debug.Log("动画播放完执行代码");
});
}
}
public virtual void OnAnimatorFinish()
{
StartCoroutine(spawnanimator());
IEnumerator spawnanimator()
{
yield return new WaitForSeconds(0.02f);
if (Eventlist.Count == 0)
{
Eventlist.Add(DefaultAction);
Debug.LogWarning(name+$"并没有新的事件,目前在执行默认的事件");
}
try
{
PlayAnimator(ani, EventAnimator[ Eventlist[0]],
() =>
{
Debug.LogError($"执行前{EventAnimator[ Eventlist[0]]}");
Eventlist[0]();
},
() =>
{
Debug.LogError("执行完");
E_AnimatorFin?.Invoke();
});
}
catch (KeyNotFoundException e)
{
Debug.LogError(Eventlist[0].Method.Name+"没有匹配动画!");
}
Eventlist.RemoveAt(0);
}
}
#region Animator
public void PlayAnimator(Animator animator, string clipName, Action startAct = null, Action endAct = null)
{
StartCoroutine(PlayAnimationItor(animator, clipName, startAct, endAct));
}
/// <summary>
/// Animation动画播放迭代器
/// </summary>
/// <param name="animation">Animation组件</param>
/// <param name="clipName">clip片段名</param>
/// <param name="startAct">委托函数</param>
/// <param name="endAct">委托函数</param>
/// <returns></returns>
private IEnumerator PlayAnimationItor(Animator animator, string clipName, Action startAct=null, Action endAct=null)
{
startAct?.Invoke();
animator.Play(clipName);
// 获取目标动画的名称
string targetClipName =clipName;
yield return StartCoroutine(WaitForEndOfAnimr(targetClipName));
IEnumerator WaitForEndOfAnimr(string targetName)
{
yield return new WaitForSeconds(0.1f);//为过渡动画预留时间
AnimatorClipInfo[] clipInfo = animator.GetCurrentAnimatorClipInfo(0);
Debug.Log("Targetname>>"+targetName);
while (animator.GetCurrentAnimatorStateInfo(0).IsName(targetName))
{
// 获取当前动画片段信息
clipInfo = animator.GetCurrentAnimatorClipInfo(0);
if (clipInfo.Length > 0)
{
// 获取当前播放的动画片段的名称
string currentClipName = clipInfo[0].clip.name;
Debug.Log("当前播放的动画片段名称: " + currentClipName);
}
yield return new WaitForSeconds(0.01f);
}
}
// Debug.LogError($"{animator.GetCurrentAnimatorClipInfo(0)[0].clip.name}+{targetClipName}");
endAct?.Invoke();
}
#endregion
#region AI逻辑
/// <summary>
/// 设置没有事件列表时自动执行的事件
/// </summary>
/// <param name="t"></param>
public void SetDefaultEvent(Action t)
{
DefaultAction = t;
}
/// <summary>
/// 为怪物添加一个事件到列表
/// </summary>
/// <param name="action"></param>
public void AddEvent(Action action)
{
Eventlist.Add(action);
}
/// <summary>
/// 重置怪物事件列表为一个新的列表
/// </summary>
/// <param name="actions"></param>
public void SetEvent(Action[] actions)
{
foreach (var VARIABLE in Eventlist)
{
Eventlist.Remove(VARIABLE);
}
foreach (var VARIABLE in actions)
{
Eventlist.Add(VARIABLE);
}
}
/// <summary>
/// 强制加入一个事件到下一个行动
/// </summary>
/// <param name="add">增加的事件</param>
public void NextEvent(Action addAction)
{
Debug.Log("length="+Eventlist.Count);
if (Eventlist.Count>0)
{
Eventlist.Insert(1,addAction);
}
else
{
Eventlist.Add(addAction);
}
}
/// <summary>
/// 强制指定下一个事件且立刻执行
/// </summary>
/// <param name="addAction"></param>
public void DoNextEvent(Action addAction)
{
StopAllCoroutines();
NextEvent(addAction);
if (Eventlist.Count>1)
{
Eventlist.RemoveAt(0);
}
PlayAnimator(ani, EventAnimator[ Eventlist[0]],
() =>
{
Eventlist[0]();
Debug.Log("动画播放前执行代码"+ EventAnimator[ Eventlist[0]]);
},
() =>
{
E_AnimatorFin?.Invoke();
Debug.Log("动画播放完执行代码");
});
}
#endregion
使用示例
public class Cow : Enemy
{
private GameObject normalATK;
public void Awake()
{
normalATK = transform.Find("NormalATK").gameObject;
normalATK.SetActive(false);
EventAnimator.Add(AI_DoRandomRelex,"Start");//为事件指定对应动画
EventAnimator.Add(Animator_Idle,"idle");
EventAnimator.Add(Animator_Rest,"rest");
EventAnimator.Add(AI_FindPlayer,"walk");
EventAnimator.Add(AI_Attack,"attack");
SetDefaultEvent(AI_DoRandomRelex);//设置默认动画
AddEvent(DefaultAction);//添加默认动画
E_HPChanged += () => {Debug.LogError("收到伤害"); };
E_HPChanged += ToHostile;//收到伤害时敌对
}
/// <summary>
/// 收到伤害的时候调用,改为攻击模式
/// </summary>
void ToHostile()
{
DoNextEvent(AI_FindPlayer);
SetDefaultEvent(AI_FindPlayer);
FriendlyTag = FriendlyLevel.Hostile;
}
public void AI_DoRandomRelex()//中立状态时随机做待机动画
{
int rd = Random.Range(0, 100);
if (rd<51)
{
Debug.Log("next>>idle");
NextEvent(Animator_Idle);
}
else
{
Debug.Log("next>>rest");
NextEvent(Animator_Rest);
}
}
private void Animator_Idle()//待机动画只有动画效果,没有要执行的内容
{
Debug.Log("IDLE");
}
private void Animator_Rest()
{
Debug.Log("REST");
}
private void Debug3()
{
}
bool finder;//只有在事件进行的时候才追踪.当事件结束后退出追踪循环
public override void AI_FindPlayer()//在敌对状态默认追踪玩家
{
finder = true;
StartCoroutine(basefind());
E_AnimatorFin += setfinder;
IEnumerator basefind()
{
while (finder)
{
base.AI_FindPlayer();
yield return new WaitForSeconds(0.1f);
}
E_AnimatorFin -= setfinder;
}
void setfinder()
{
finder = false;
}
StartCoroutine(atrange());
E_AnimatorFin += () => { StopCoroutine(atrange()); };
IEnumerator atrange()
{
do
{
if (IsPlayerInAttackRange)//如果玩家在攻击范围内就进行攻击事件
{
DoNextEvent(AI_Attack);
}
yield return new WaitForSeconds(0.02f);
} while (true);
}
}
public void AI_Attack()//攻击事件
{
base.AI_FindPlayer();
Debug.LogError("AIATK");
StartCoroutine(WaitATK());
E_AnimatorFin += () => {des(); };
IEnumerator WaitATK()
{
yield return new WaitForSeconds(0.2f * EventSpeed);
normalATK.SetActive(true);
}
void des()
{
normalATK.SetActive(false);
E_AnimatorFin -= des;
}
}
public override void Update()
{
base.Update();
//如果玩家在索敌范围外就变回中立
if (!IsPlayerInFindingRange&& (FriendlyTag == FriendlyLevel.Hostile))
{
FriendlyTag = FriendlyLevel.Neutral;
SetDefaultEvent(AI_DoRandomRelex);
}
}
}