先给出素材:
Tip Panel:
动画状态机:
用于控制Tip Panel的动画,经过将Exit变量设置为True时执行Tip Panel的退出动画。
ClosePanel()代码:
某用于统一管理Panel的管理类中统一关闭Panel的方法。经过传入参数name(要关闭的Panel的名字),先调用对应panel的OnClosing方法执行一些逻辑后才Destroy掉panel的实例。web
/// <summary>
/// close a panel.
/// </summary>
/// <param name="name">type string name, panel's name.</param>
public void ClosePanel(string name)
{
//...
panel.OnClosing ();
GameObject.Destroy (panel.gameObject);
}
OnClosing()代码:
panel关闭前执行的逻辑在这里实现。
其中,exit2Hash是状态Exit变量的hash值。svg
public void OnClosing ()
{
animator.SetBool (exit2Hash, true);
Debug.Log (animator.GetBool (exit2Hash).ToString ());
Debug.Log (animator.GetCurrentAnimatorClipInfo (0) [0].clip.name);
}
OK按钮的点击事件是动态添加的:动画
okBtn.onClick.AddListener (OnOKButtonClick);
OnOKButtonClick()代码:
注意,ClosePanel方法和其余方法(如OnClosing、OnOKButtonClick以及ok安寝的动态添加点击事件都在同一个Panel类中)不在同一个类中。spa
void OnOKButtonClick()
{
XXX.ClosePanel(gameObject.name);
}
在实现以下流程时,碰到了问题:
点击ok按钮 -> 执行Panel关闭动画 -> Destroy Panel对象.net
问题1:
动画还未执行完毕,Panel便被Destroy掉了。
解决方案一:
经过给tippanel_exit_clip动画添加事件,在tippanel_exit_clip动画播放解释后,触发指定事件Destroy掉panel实例,但这样显然就破坏掉了ClosePanel自己的意义。
解决方案二:
在ClosePanel方法中的Destroy方法添加延迟参数,等待一段时间再Destroy掉panel实例。debug
GameObject.Destroy (panel.gameObject, 1f);
缺点在于不可以根据动画具体结束时间再去Destroy,若是panel的Exit动画的执行时间超过1f,仍然会出现动画未执行完毕便被Destroy的现象。3d
尝试:
判断tippanel_exit_clip是否播放完毕,播放完毕后再执行后续代码。
有问题!animator.SetBool(exit2Hash, true)执行后,状态变量Exit的值的确是变为了True,可是实际上,animator并无转换到tippanel_exit_clip,animator此时的clip仍然是tippanel_enter_clip。这就很迷了。。。
修改OnClosing代码以下:调试
public void OnClosing ()
{
base.OnClosing ();
animator.SetBool (exit2Hash, true);
Debug.Log (animator.GetBool (exit2Hash).ToString ());
Debug.Log (animator.GetCurrentAnimatorClipInfo (0) [0].clip.name);
}
获得的结果为:
code
这里显现出一个问题,动画过渡。
Unity3d中Animator进行动画切换时会有一个过渡阶段,也就是Transition,在完成Transition以前,状态机的State会一直保持原State不变直到完成Transition后才会改变State,这也就是为何咱们看到Exit的值改变后,状态机对应的State并无转换到对应State的缘由了。
解决这个问题,我选择经过设置Transition的Transition Duration来处理。
参考文章hongfei233: Unity3D——Mecanim动画 AnimatorTransitionInfo和AnimatorStateInfo 在角色移动和待机平滑切换中的应用orm
问题:
即便把Transition Duration的值设置为0,调试结果仍然为
嗯。。。很迷。。。虽然修改了TransitionDuration的值,点击按钮后,响应tippanel_exit_clip动画执行的速度变快了,可是debug的值仍然没有变,这样便不能经过检测animator当前的状态来实现等待动画执行完毕再执行Destroy方法了。。。
据说使用animator.Play()能够当即切换clip,兴奋的我赶忙去试了一下结果…
Debug:
说明一下:exit2Hash = Animator.StringToHash(“tippanel_exit_clip”);
。。没错,视觉上动画的确转换了,然而为什么debug出来的信息仍然显示为tippanel_enter_clip。。。。。(被困扰的次日)
结果:
Unity3d在状态切换时,会进行两个状态的融合也就是动画过渡,而Transition就是这个混合过程,只有当Transition完成混合后,才会真正的切换到另外一个状态。即便Animator.Play能够忽略混合过程直接切换状态,但事实上,当前帧是检测不出来的,只有在下一帧才能检测到状态切换为另外一个状态。
最后选择的处理方法:设置一个bool变量来标记状态,使用协程判断当前的动画是否为tippanel_exit_cip且播放完毕,达到条件则改变标记状态。在Update中检测标记变量的值来决定下一步是作其余仍是Destroy掉Panel。
代码以下:
// XXX CLASS
public void ClosePanel(string name)
{
//...
panel.OnClosing ();
GameObject.Destroy (panel.gameObject);
}
//======================================
private int exit2Hash = Animator.StringToHash("tippanel_exit_clip");
private bool isFinished = false;
//...
void Update()
{
if(isFinished)
{
XXX.ClosePanel();
}
}
public void OnClosing ()
{
animator.SetBool (exit2Hash, true);
StartCoroutine (PlayExitAnimation ());
}
void OnOKButtonClick()
{
OnClosing();
}
IEnumerator PlayExitAnimation()
{
while (true) {
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo (0);
if (stateInfo.IsName ("tippanel_exit_clip") && stateInfo.normalizedTime >= 1.0f) {
break;
}
yield return null;
}
isFinished = true;
}