Unity IK设置目标的旋转与实际不一致

Unity 设置目标IK的position和rotation

使用Unity官方自带的IK例子,资源地址下载,IK的目标rotation会有问题。

以左手为例子,
IK代码

using UnityEngine;
using System;
using System.Collections;

[RequireComponent(typeof(Animator))]
public class IKTest : MonoBehaviour
{

    protected Animator animator;

    public bool ikActive = false;
    public Transform LeftHandObj = null;
    public Transform lookObj = null;

    void Start()
    {
        animator = GetComponent<Animator>();
    }

    //a callback for calculating IK
    void OnAnimatorIK()
    {
        if (animator)
        {

            //if the IK is active, set the position and rotation directly to the goal. 
            if (ikActive)
            {

                // Set the look target position, if one has been assigned
                if (lookObj != null)
                {
                    animator.SetLookAtWeight(1);
                    animator.SetLookAtPosition(lookObj.position);
                }

                // Set the right hand target position and rotation, if one has been assigned
                if (LeftHandObj != null)
                {
                    animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1);
                    animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1);
                    animator.SetIKPosition(AvatarIKGoal.LeftHand, LeftHandObj.position);
                    animator.SetIKRotation(AvatarIKGoal.LeftHand, LeftHandObj.rotation);

                }

            }

            //if the IK is not active, set the position and rotation of the hand and head back to the original position
            else
            {
                animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 0);
                animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 0);
                animator.SetLookAtWeight(0);
            }
        }
    }

    void LateUpdate()
    {
        Debug.DrawRay(LeftHandObj.position, LeftHandObj.forward * 10, Color.green);
        Debug.DrawRay(animator.GetBoneTransform(HumanBodyBones.LeftHand).position, animator.GetBoneTransform(HumanBodyBones.LeftHand).forward * 10, Color.red);
    }


}

设置左手的目标position和rotation,发现虽然位置和targe的position一样,但是旋转和target的旋转不一样。绿色为期望的方向,红色为左手当前的方向。如图:
这里写图片描述

google搜索了一阵子,一个解决方案是在LateUpdate中直接设置左手旋转,不使用Unity的SetIKRotation

void LateUpdate()
    {
        animator.GetBoneTransform(HumanBodyBones.LeftHand).rotation =
            LeftHandObj.rotation;
        Debug.DrawRay(LeftHandObj.position, LeftHandObj.forward * 10, Color.green);
        Debug.DrawRay(animator.GetBoneTransform(HumanBodyBones.LeftHand).position, animator.GetBoneTransform(HumanBodyBones.LeftHand).forward * 10, Color.red);
    }

如下图:
这里写图片描述

但是这个不是通过IK旋转出来的结果,可能会有问题。

还有一个解决方法
Unity dev的一个解决方案:

There is effectively a constant rotation offset between original skeleton bone rotation and mecanim IK goal rotation.

Goal Rotation = GR
Skeleton Rotation = SR
Offset Rotation = OR

where:

GR = SR * OR

or:

OR = INV(SR) * GR

You can compute this constant offset in the 1st IK pass since IK goals are synced to skeleton at the beginning of IK solve.

GR = GetGoalRotation(handGoalIndex)
SR = GetBoneTransform(handIndex).rotation

Then you can use OR to set a goal within skeleton referential using:

SetIKGoalRotation(SomeRot*OR)

Note: Mecanim Humanoid abstracts the original skeleton rig for many reason. One of these is to be able to create IK rigs that do not depend on original skeleton, but instead only depend a normalized humanoid rig that fits all humanoid characters.

意思是IK Rotation会有一个固定的旋转offset,导致了目标旋转和左手当前的旋转不一致,按照上面的思路,添加一个offset,效果如下:
这里写图片描述
可以看到旋转大致上变一致了,但是还是有一些偏差。播放其他动画的时候也会有一定的偏差。
代码:

if (LeftHandObj != null)
                {
                    animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1);
                    animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1);
                    var gr = animator.GetIKRotation(AvatarIKGoal.LeftHand);
                    var sr = animator.GetBoneTransform(HumanBodyBones.LeftHand).rotation;

                    var or = Quaternion.Inverse(sr) * gr;
                    animator.SetIKPosition(AvatarIKGoal.LeftHand, LeftHandObj.position);
                    animator.SetIKRotation(AvatarIKGoal.LeftHand, LeftHandObj.rotation * or);

                }

具体原理需要看unity源代码了-_-


猜你喜欢

转载自blog.csdn.net/a352614834/article/details/80784117
今日推荐