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

Unity 设置目标IK的position和rotation


使用Unity官方自带的 IK例子, 资源地址下载,IK的目标rotation会有问题。
动画状态机设置如下:
这里写图片描述
第0层是一个动画层,包含idle,walk,跳跃等动画。
第1层是左手遮罩层次,是一个左手的pose
以左手为例子,
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。对于idle动画是正确的,但是对于奔跑动画旋转还是不对,效果如下:
这里写图片描述
对于idle动画是正确的,播放奔跑时候也会有偏差!。
代码:

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);
                    
                }

播放奔跑效果不对,最后发现有二种解决方法,

  1. 是BaseLayer不勾选IK Pass, HandPose的mask 左右手不点上IK,实际效果旋转只有一点点的偏差
  2. 是BaseLayer勾选IK Pass, HandPose的mask 左右手点上IK,实际效果完美,具体原因未知,应该是Unity对IK的实现有问题。
    这里写图片描述
    这里写图片描述

demo下载地址:https://download.csdn.net/download/a352614834/10520538

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值