ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

黑魂复刻游戏的玩家控制器(播放攻击动画时的位移)——Unity随手记

2021-06-15 10:34:21  阅读:248  来源: 互联网

标签:动画 deltaPos Motion Unity 黑魂 位移 OnAnimatorMove Root 复刻


今天实现的内容:

通过代码运用动画自带的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");

    }

 

标签:动画,deltaPos,Motion,Unity,黑魂,位移,OnAnimatorMove,Root,复刻
来源: https://www.cnblogs.com/heyu123/p/14884462.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有