【问题-已解决】Unity3d Animator的状态转换时,状态变量改变而State没有改变

先给出素材:
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;
}