一时兴起,写了这一篇文章。在实际开发中为了更好的表现,需要在一段动画播放后,再做别的行为
这篇文章3月6日开始定题,3月22日写完初稿,5月4日再补充一点,今天发布。
一.不同的动画组件
- 动画美术直接编辑动画,定位到要变化挂点的帧(比如24帧),在Events里添加一个动画事件, 名字规范成 TakeWeapon
这个方案
优:让美术准确控制帧数, 可以个性化
劣:所有模型该动作都要加一下。
二.用程序添加
网上有animation添加事件的
1 2 3 4
| AnimationEvent evt = new AnimationEvent(); evt.time = 0; evt.functionName = "Test"; animation.GetClip("ani").AddEvent(evt);
|
但我们现在用的新动画系统Animator, 如何找到对应名字的动画呢?
找了一下 Animator的接口和成员变量,还是有一个可用的:
1 2 3 4 5 6 7
| for(int i=0;i< animator.runtimeAnimatorController.animationClips.Length;++i) { if(animator.runtimeAnimatorController.animationClips[i].name == name) { return animator.runtimeAnimatorController.animationClips[i]; } }
|
能找到对应的名字的 animationClip,然后就可以
1 2 3 4
| AnimationEvent evt = new AnimationEvent(); evt.time = 24f/30; evt.functionName = "TakeWeapon"; animClip.AddEvent(evt);
|
优:所有角色动画动作可以统一处理,不需要美术再加工
劣:时间不太好做个性化。
事件绑定好了,但是执行时报错误:
AnimationEvent ‘TakeWeapon’ has no receiver!
原来 TakeWeapon方法所在脚本必须挂在Animation(或Animator)所在GameObject上!
三.当使用Animator组件的时候
1 2 3 4 5 6 7 8
| void Update() { var stateInfo = an.GetCurrentAnimatorStateInfo(0); if(stateInfo.IsName(_openName)&& stateInfo.normalizedTime >= 1f) { //播放结束 } }
|
四.使用IEnumerator 等待动画的长度
1 2 3 4 5 6 7 8 9 10 11 12
| Animation Anim; float time = Anim.Getclip("anim1").Length; private IEnumerator WaitAnimationEnd() { yield return new WaitForSeconds(time); EndAnimation(); } private void EndAnimation() { // 动作播放结束 }
|
五.使用Animation踩的坑
1. 有一个头像大小缩放的动画,从Animation是没有任何问题的,但实际代码调用的时候,一直不执行。
功能开发中,优先级并不是特别高。就一直没查。
后来查找原因,当界面没有打开的状态下,执行动画的播放.当界面打开的时候再播放,动画部生效了,所以在animation调用的时候,要一句
1
| if(this.gameObject.activeInHierarchy == false)return;
|
2.基于四的思路,做了一个文字动画反复播放显示的内容。当同时添加两段文字的时候,只播放了一次。设计上是将文字做了队列缓存 。当一个动画播完在播放另一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| private Animation Anim; public Queue<string> TipsStringQueue = new Queue<string>(); //Add String public void AddString(string tips) { TipsStringQueue.Enqueue(tips); if(!Anim.isPlaying) { ParseString(); } } private void ParseString() { if(TipsStringQueue.count == 0) return; string tips = TipsStringQueue.Dequeue(); if(!string.IsNullOrEmpty(tips)) { //Json解析 //UI元素赋值 Anim["Anim01"].time = 0; Anim.Play(); StopCoroutine(WaitAnimationEnd()); StartCoroutine(WaitAnimationEnd()); } } private IEnumerator WaitAnimationEnd() { yield return new WaitForSeconds(Anim.GetClip("Anim01").length); EndAnimation(); } private void EndAnimation() { if(TipsStringQueue.count>0) { ParseString(); } }
|
Anim[“Anim01”].time = 0;在Play()之前写这句很重要的,不然就会出现当同时加入两个文字的时候,播放一次。
补充 把Loop的Animation停止指定帧
当时实习生遇到,把一个Loop的动画停在初始帧。
把循环动画停在指定的帧上,先给time赋值,在simple(),在stop()。
1 2 3 4 5 6 7
| private void StoLoopAnimationOnBegin() { Animation an; an["an1"].time = 0; an.Sample(); an.Stop(); }
|
原因是
在调用了动画播放之后,动画并不会立即应用(骨骼Transform并不会立即改变),最快也要等到本帧lateUpdate才能生效。
如果有特殊需求,希望在调用了动画播放之后立即生效,则可以紧接着调一句Animation.sample()