1. 程式人生 > 其它 >Unity-動畫狀態機使用細節記錄

Unity-動畫狀態機使用細節記錄

Unity動畫控制器Animator功能非常強大,總結一些具體使用細節,在動作遊戲中很實用;

1.動畫烘焙

不同動畫之間,可能存在角色朝向,重心高度不一致;

可以在動畫Eidt介面設定RootTransform Rotation重新烘焙旋轉;

RootTransformPosition(Y)烘焙高度,地面動作選擇feet,烘焙在腳上;

設定完烘焙必須點Apply才能生效;

2.動作映象

有些單手動作,左右手都可以做,動畫機新增bool引數,動作Inspector介面Mirror勾選Parameter;

將bool引數設定進去,程式碼動態調節bool引數切換左右手;

3.StateMachineBehaviour

Unity動畫機沒有直接的api判斷動畫播放結束,StateMachineBehaviour可以做到;

動畫狀態機生命週期:

OnStateEnter

OnStateUpdate

OnStateExit

建立FSMOnExit指令碼繼承StateMachineBehaviour,重寫OnStateExit方法,監聽到有退出訊息向上傳遞方法名,在角色控制指令碼中實現該方法;

該指令碼掛載在動畫機中需要監聽的state上;

TIps:SendMessageUpwards效率很低,建議改成事件機制;

public class FSMOnExit : StateMachineBehaviour
{
    public string[] onExitMessages;

    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        for (int i = 0; i < onExitMessages.Length; ++i)
        {
            animator.gameObject.SendMessageUpwards(onExitMessages[i]);
        }
    }
}

4.trigger累積清空

Unity動畫機的trigger有個出名的”bug“,trigger會累積1次;

造成的效果就是,起跳後空中按跳,落地會再次起跳;

同樣攻擊也會如此;

解決辦法與上述方法相同,在OnStateExit、OnStateEnter按需求重置trigger;

也可以選中在關鍵幀動畫事件中重置Trigger,可以實現連擊時機判定(前置動作過半再次按下攻擊才可連擊);

public class FSMClearSignals : StateMachineBehaviour
{
    public string[] clearAtEnter;
    public string[] clearAtExit;

    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        foreach (string key in clearAtEnter)
        {
            animator.ResetTrigger(key);
        }
    }

    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        foreach (string key in clearAtExit)
        {
            animator.ResetTrigger(key);
        }
    } 
}

5.連擊動作混合

連擊動作之間不需要收招,如果是同一個Layer的動作,使用官方混合,拖動時間線同時可以預覽效果;

ExitTime:該狀態有多個後續連線狀態,中間沒有其他條件的情況下,ExitTime決定先進入的狀態;

InterruptionSoure:在混合狀態時(重疊部分),該狀態被打斷返回的狀態,預設返回Next;

可設定Current State,Next State;

6.動畫Layer設定

Layer可用隔開常規行為動作、攻擊、面部表情、以及不同職業動作;

建立動畫層可以設定weight權重,和Mask骨骼遮罩;

Defend層,由於防禦只需要右手動作一直舉盾,其他動作一致,新增RightHandMask;

Asset面板右鍵Create-Avatar Mask,把不需要的骨骼都標紅;

層與層之間會更具weight混合——這就讓我們可以通過程式碼來插值計算在不同之間做動畫連貫處理;

比如BaseLayer=>Attack層做了插值計算後;

7.動畫曲線

動畫過程中可以設定曲線,通過程式碼可獲得曲線的值;

比如這裡,我希望攻擊時,角色向前跨出一步,但是又不希望勻速滑步,要求起手時跨出一大段距離,逐漸減少,有個慣性的效果;設定如下曲線;

程式碼中:通過動畫機獲取引數attack1hAVelocity,會返回動畫時間對應的曲線值;

model.transform.forward * mAnima.GetFloat("attack1hAVelocity");

8.RootMotion

有些動畫播放時位置會改變,Animator動畫機勾選Apply Root Motion,表示使用動畫中的位移;

Unity指令碼生命週期中有一個OnAnimaRootMove,實現該方法動畫中的位移只會受到指令碼控制;

程式碼中,當播放attack1hC動畫時,OnAnimaRootMove累積動畫中的位置,在FixedUpdate中賦予Rigidbody;

這樣可以做到前面動畫曲線的效果,這種方法會更符合美術需求;

9.onAnimationIK

在OnAnimationIK週期方法中程式碼控制骨骼節點的位置;

我就動作不符合需求且沒有美術配合的時候用用;

也有很多高階IK演算法,可以模擬出各種炫酷飄逸的效果;油管大佬

public class LeftArmAnimaFix : MonoBehaviour
{
    private Transform leftArmLow;
    [SerializeField]private Vector3 offset;
    private Animator mAnima;

    private void Start()
    {
        mAnima = GetComponent<Animator>();
        leftArmLow = mAnima.GetBoneTransform(HumanBodyBones.LeftLowerArm);
    }

    public void OnAnimatorIK(int layerIndex)
    {
        leftArmLow.localEulerAngles += offset;
        mAnima.SetBoneLocalRotation(HumanBodyBones.LeftLowerArm,Quaternion.Euler(leftArmLow.localEulerAngles));
    }
}