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

先给出素材:
Tip Panel:
【问题-已解决】Unity3d Animator的状态转换时,状态变量改变而State没有改变_第1张图片
动画状态机:
用于控制Tip Panel的动画,通过将Exit变量设置为True时执行Tip Panel的退出动画。
【问题-已解决】Unity3d Animator的状态转换时,状态变量改变而State没有改变_第2张图片
ClosePanel()代码:
某用于统一管理Panel的管理类中统一关闭Panel的方法。通过传入参数name(要关闭的Panel的名字),先调用对应panel的OnClosing方法执行一些逻辑后才Destroy掉panel的实例。

/// 
/// close a panel.
/// 
/// type string name, panel's name.
public void ClosePanel(string name)
{
    //...

    panel.OnClosing ();                                         
    GameObject.Destroy (panel.gameObject);  
}

OnClosing()代码:
panel关闭前执行的逻辑在这里实现。
其中,exit2Hash是状态Exit变量的hash值。

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类中)不在同一个类中。

void OnOKButtonClick()
{
    XXX.ClosePanel(gameObject.name);
}

在实现如下流程时,碰到了问题:
 点击ok按钮 -> 执行Panel关闭动画 -> Destroy Panel对象

问题1:
 动画还未执行完毕,Panel便被Destroy掉了。
解决方案一:
 通过给tippanel_exit_clip动画添加事件,在tippanel_exit_clip动画播放解释后,触发指定事件Destroy掉panel实例,但这样显然就破坏掉了ClosePanel本身的意义。
解决方案二:
 在ClosePanel方法中的Destroy方法添加延迟参数,等待一段时间再Destroy掉panel实例。

GameObject.Destroy (panel.gameObject, 1f);  

 缺点在于不能够根据动画具体结束时间再去Destroy,如果panel的Exit动画的执行时间超过1f,仍然会出现动画未执行完毕便被Destroy的现象。

尝试:
 判断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);
}

得到的结果为:
这里写图片描述

这里显现出一个问题,动画过渡。
Unity3d中Animator进行动画切换时会有一个过渡阶段,也就是Transition,在完成Transition之前,状态机的State会一直保持原State不变直到完成Transition后才会改变State,这也就是为什么我们看到Exit的值改变后,状态机对应的State并没有转换到对应State的原因了。
解决这个问题,我选择通过设置Transition的Transition Duration来处理。
参考文章hongfei233: Unity3D——Mecanim动画 AnimatorTransitionInfo和AnimatorStateInfo 在角色移动和待机平滑切换中的应用

问题:
 即使把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;
}

你可能感兴趣的:(问题集,Unity3d)