乐趣区

关于unity:黑魂复刻游戏的玩家控制器播放攻击动画时的位移Unity随手记

明天实现的内容:
通过代码使用动画自带的 Root Motion
在这篇博客之前,攻打时是不会呈现位移的,因为咱们没有使用动画自带的 Root Motion。为了更好的动画成果,咱们接下来将会使用到 Root Motion。

上图中,红色的标识批示了动画的 Root Motion 量(具体我也不是太懂,反正就是个量),当咱们使用 Root Motion 时,零碎会将 Root Motion 量套用到网络游戏对象的 transform 上,留神是 Animator 所在的游戏对象,咱们的我的项目中,Animator 被放到控制器次级的模型对象上,所以挪动的只是模型。
要应用 Root Motion,咱们须要设置 Animator 组件下的 Apply Root Motion 为 true,然而如果咱们间接就这么做了,那些自身带有 Root Motion 然而咱们并不想要使用的动画也会被用上,比如说挪动动画,而且因为咱们的模型只是控制器对象的子对象,使用动画的 Root Motion 会导致模型脱离父对象的原点。为了解决这些问题,咱们还是须要用脚本来使用 Root Motion。上面的图片展现的就是我刚刚提到的问题。


咱们的代码将应用 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。

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。

上面是 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 的成果那么好了,毕竟美术大大们在这方面还是比咱们厉害的。

    // 在 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");

    }

退出移动版