黑魂復刻遊戲的玩家控制器(播放攻擊動畫時的位移)——Unity隨手記

今天實現的內容:
經過代碼運用動畫自帶的Root Motion
在這篇博客以前,攻擊時是不會出現位移的,由於咱們沒有運用動畫自帶的Root Motion。爲了更好的動畫效果,咱們接下來將會運用到Root Motion。
image.png
上圖中,紅色的標識指示了動畫的Root Motion量(具體我也不是太懂,反正就是個量),當咱們運用Root Motion時,系統會將Root Motion量套用到網絡遊戲對象的transform上,注意是Animator所在的遊戲對象,咱們的項目中,Animator被放到控制器次級的模型對象上,因此移動的只是模型。
要使用Root Motion,咱們須要設置Animator組件下的Apply Root Motion爲true,可是若是咱們直接就這麼作了,那些自己帶有Root Motion可是咱們並不想要運用的動畫也會被用上,好比說移動動畫,並且因爲咱們的模型只是控制器對象的子對象,運用動畫的Root Motion會致使模型脫離父對象的原點。爲了解決這些問題,咱們仍是須要用腳原本運用Root Motion。下面的圖片展現的就是我剛剛提到的問題。
image.png
image.png
咱們的代碼將使用OnAnimatorMove方法(MonoBehaviour.OnAnimatorMove),該方法會在動畫機算完Root Motion值以後的每一幀調用(可是仍是在IK計算以前)。在這裏咱們甚至能夠對動畫機算完以後的Root Motion作修改。爲何是算完以後?由於在算以前修改等於沒改。
此次咱們不會去修改Root Motion,咱們將利用OnAnimatorMove的系統調用時機,首先獲得動畫機計算的deltaPosition,就是動畫機計算出的模型的Root Motion的位移(Root Motion不單單是位移還有旋轉deltaRotation,可是此次咱們只要位移就行),繼續發揮傳統藝能,在OnAnimatorMove中將deltaPosition,經過發送消息傳遞給PlayerController,由PlayerController中的OnUpdateRootMotion根據deltaPosition去移動Rigidbody。畢竟Rigidbody在同一級,位移相關的全部操做仍是要交給PlayerController。這樣一來,咱們就經過腳本運用了攻擊動畫的Root Motion。segmentfault

public class RootMotionController : MonoBehaviour
{
    private Animator anim;

    private void Awake()
    {
        anim = GetComponent<Animator>();
    }

    void OnAnimatorMove()
    {
        SendMessageUpwards("OnUpdateRootMotion", (object)anim.deltaPosition);
    }
}

能夠發現當咱們用了OnAnimatorMove之後,Apply Root Motion變成了Handled by Script。
image.png
下面是PlayerController中的OnUpdateRootMotion具體是如何運用Root Motion的。注意,咱們是在每一物理幀中才進行Rigidbody的位置修改,因此在每個動畫幀中要作的事情是累加Root Motion的位移量。網絡

// Root Motion的位移量 用於腳本運用Root Motion
    private Vector3 m_deltaPos;

        // 處理剛體的操做
    private void FixedUpdate()
    {
        // 運用Root Motion 要放到修改rb.velocity之前進行
        rb.position += m_deltaPos;
        
        // ...
        
        // 清零當前物理幀累積的m_deltaPos
        m_deltaPos = Vector3.zero;
    }

    // 經過腳本運用動畫的Root Motion
    // 經過RootMotionController腳本中的OnAnimatorMove調用
    public void OnUpdateRootMotion(object _deltaPos)
    {
        // 當前處於attack_oneHand_C動畫纔會運用Root Motion位移
        if (CheckState("attack_oneHand_C", "Attack"))
        {
            // 更新m_deltaPos爲動畫機的Root Motion 之因此用累加是由於物理幀和動畫幀不同 在物理幀的最後會將m_deltaPos清零
            // 根據我那點可憐的C#基礎知識 這一步會致使拆箱 OnAnimatorMove中的那一步會致使裝箱 損耗計算資源
            m_deltaPos += (Vector3)_deltaPos;
        }
    }

最後的關鍵一步,咱們要識別當前處在什麼動畫狀態,只有在攻擊時if (CheckState("attack_oneHand_C", "Attack"))才更新Root Motion,這樣作完之後,就規避了直接Apply Root Motion帶來的問題。動畫

按照以前的辦法自制位移
若是動畫沒有自帶位移,咱們就只能使用以前曲線加衝量的老辦法自制了。可是效果確定就很難有美術們作Root Motion的效果那麼好了,畢竟美術大大們在這方面仍是比咱們厲害的。url

// 在Attack層的動畫節點attack_oneHand_B更新時執行的方法
    // 經過PlayerController動畫機中的attack_oneHand_B節點上掛載的FSMOnUpdate調用
    public void OnAttack_oneHandBUpdate()
    {
        // 計算攻擊時的衝量
        m_thrustVec = model.transform.forward * anim.GetFloat("attackOneHandAVelocity"); ;

    }

    // 在Attack層的動畫節點attack_oneHand_C更新時執行的方法
    // 經過PlayerController動畫機中的attack_oneHand_C節點上掛載的FSMOnUpdate調用
    public void OnAttack_oneHandCUpdate()
    {
        // 計算攻擊時的衝量
        m_thrustVec = model.transform.forward * anim.GetFloat("attackOneHandAVelocity");

    }

image.png

相關文章
相關標籤/搜索