Unity—反向动力学IK

每日一句:人生最精彩的不是实现梦想的瞬间,而是坚持梦想的过程

目录

定义:

准备:

API:

设置IK

头部IK——设置人物的头部根据视角旋转

手脚IK

案例:脚步IK


定义:

一般来说,骨骼动画都是传统的从父节点到子节点的带动方式(即正向动力学),IK则倒过来,由骨骼子节点带动骨骼父节点。

根据骨骼的终节点来推算其他父节点的位置的一种方式

比如人物走路踩到了石头,就需要由脚的子节点带动全身骨骼做出踩到石头的响应。

准备:

·Model的Aniamtion Type设置为Humanoid

·检测Avatar是否异常

·Animator勾选Ik Pass

API:

OnAnimatorIK(int layeIndex)设置动画IK的回调layeIndex:调用反向动力学解锁器的层的索引;对应Layer的序号,每个勾选了IKPass的layer调用一次

AvatarIKGoal反向动力学目标[枚举类型].LeftFoot左脚/RightFoot右脚/LeftHand左手/RightHand右手

SetIKPositionWeight(AvatarIKGoal goal,float value)设置反向动力学目标的转换权重

SetIKPosition(AvatarIKGoal goal,Vector3 goalPosition)设置反向动力学目标的位置

SetLookAtPosition(Vector3 lookAtPosition)执行LookAt的位置

public Vector3 bodyPosition;身体质心的位置

public Quaternion bodyRotation;身体质心的旋转

(只应在OnAnimatorIK()函数调用中设置该值)

设置IK

头部IK——设置人物的头部根据视角旋转

用到两个API  

SetLookAtPosition  

SertLookAtWeight用来设置IK的权重,IK会和原来的动画进行混合。如果权重为1,则完全用IK的位置和旋转,如果权重为0,则完全用原来动画中的位置和旋转

void OnAnimatorIK(int layerIndex)

{

ani.SetLookAtPosition(pos);

ani.SetLookAtWeight(1);

}

手脚IK

SetIkPosition(AvatarIKGoal goal,Vector3 goalPosition);Ik目标位置

SetIKRotation(AvatarIKGoal goal,Quaternion goalRoattion);IK目标旋转

设置权重的API

SetIKPositionWeight(AvatarIKGoal goal,float value);

SetIKRotationWeight(AvatarIKGoal goal,float value);

常见的手部IK代码

void OnAnimatorIK(int layerIndex)

{

ani.SetIKPosition(AvatarIKGoal.LeftHand,position);

ani.SetIKPositionWeight(AvatarIKGoal.LeftHand,1);

ani.SetIKRotation(AvatarIKGoal.LeftHand,rotation);

ani.SetIKRotationWeight(AvatarIKGoal.LeftHand,1);

}

案例:脚步IK

private Animator theAnimator;

    private Vector3 leftFootIK, rightFootIK;//射线检测需要的IK位置

    private Vector3 leftFootPosition, rightFootPosition;//Ik位置赋值

    private Quaternion leftFootRotation, rightFootRotation;//IK旋转赋值

    [SerializeField]private LayerMask ikLayer;//射线可以检测到的层

    [SerializeField] [Range(0, 0.2f)] private float rayHitOffset;//射线检测位置与IK位置的偏移

    [SerializeField] private float rayCastDistance;//射线检测距离

    [SerializeField] private bool enableIK = true;//是否启用IK

    [SerializeField] private float ikSphereRadius = 0.05f;

    [SerializeField] private float positionSphereRadius = 0.05f;

    private void Awake()

    {

        theAnimator = this.gameObject.GetComponent<Animator>();

        leftFootIK = theAnimator.GetIKPosition(AvatarIKGoal.LeftFoot);

        rightFootIK = theAnimator.GetIKPosition(AvatarIKGoal.RightFoot);

    }

    private void OnAnimatorIK(int layerIndex)

    {

        leftFootIK = theAnimator.GetIKPosition(AvatarIKGoal.LeftFoot);

        rightFootIK = theAnimator.GetIKPosition(AvatarIKGoal.RightFoot);

        if (!enableIK)

        {

            return;

        }

        //设置IK权重

        theAnimator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, theAnimator.GetFloat("LIK"));

        theAnimator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, theAnimator.GetFloat("LIK"));

        theAnimator.SetIKPositionWeight(AvatarIKGoal.RightFoot, theAnimator.GetFloat("RIK"));

        theAnimator.SetIKRotationWeight(AvatarIKGoal.RightFoot, theAnimator.GetFloat("RIK"));

因为权重是1,动画为跑时,脚的动画将被IK完全影响

因此可以创建一条动画曲线,为其添加关键帧,去控制IK的值

当脚与地面完全贴合时,IK权重为1,离开地面时,IK权重为0

在Animator中创建一个和动画曲线名称一样的参数,它会在动画播放时自动获取对应曲线的值,之后在代码中修改权重

        //设置IK位置和旋转值

        theAnimator.SetIKPosition(AvatarIKGoal.LeftFoot, leftFootPosition);

        theAnimator.SetIKRotation(AvatarIKGoal.LeftFoot, leftFootRotation);

        theAnimator.SetIKPosition(AvatarIKGoal.RightFoot, rightFootPosition);

        theAnimator.SetIKRotation(AvatarIKGoal.RightFoot, rightFootRotation);

    }

    // Update is called once per frame

    void Update()

    {

        Debug.DrawLine(leftFootIK + (Vector3.up * 0.5f), leftFootIK + Vector3.down * rayCastDistance, Color.blue, Time.deltaTime);

        Debug.DrawLine(rightFootIK + (Vector3.up * 0.5f), rightFootIK + Vector3.down * rayCastDistance, Color.blue, Time.deltaTime);

        if(Physics.Raycast(leftFootIK+(Vector3.up*0.5f),Vector3.down,out RaycastHit hit,rayCastDistance+1,ikLayer))

        {

            Debug.DrawRay(hit.point, hit.normal, Color.red, Time.deltaTime);

            leftFootPosition = hit.point + Vector3.up * rayHitOffset;

            leftFootRotation = Quaternion.FromToRotation(Vector3.up, hit.normal) * transform.rotation;

        }

    //当检测到指定层物体时,返回碰撞点的各种信息,脚步的旋转值与返回的法线信息有关

     if (Physics.Raycast(rightFootIK + (Vector3.up * 0.5f), Vector3.down, out RaycastHit hit_, rayCastDistance + 1, ikLayer))

        {

            Debug.DrawRay(hit_.point, hit_.normal, Color.red, Time.deltaTime);

            rightFootPosition = hit_.point + Vector3.up * rayHitOffset;

            rightFootRotation = Quaternion.FromToRotation(Vector3.up, hit_.normal) * transform.rotation;

        }

    }

猜你喜欢

转载自blog.csdn.net/m0_63330263/article/details/127687223