本文主要是介绍Unity Avatar Foot IK - Avatar Foot Placement Resolution,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 简介
- 实现
- Avatar FBX Import Settings
- Animator Settings
- On Animator IK
- Calculate IK Position & Rotation
- Body Position
- Apply IK Position & Rotation
简介
通过Unity内部的Mecanim动画系统实现的FootIK功能,效果如图所示,左右分别为开启和关闭FootIK的效果:
初版1.0.0代码已上传至SKFramework
框架PackageManager
中:
相关变量说明:
Enable Foot Ik
:是否启用FootIKFoot Ik Pass Layer Index
:Animator启用IKPass对应的层级Layer Mask
:射线检测时所有的层级Body Y Offset
:身体Y坐标的偏移量Body Position Lerp Speed
:身体坐标插值的速度Foot Position Lerp Speed
:脚部坐标插值的速度Raycast Distance
:射线检测的最大距离Raycast Origin Height
:射线检测的高度
实现
Avatar FBX Import Settings
- Animation Type: 需要Humanoid人形动画
- Avatar Configuration:确保配置正确
Animator Settings
- Foot IK:相应的Animator State中需要开启Foot IK
- IK Pass:相应的Animator Layer中需要开启IK Pass通道
On Animator IK
动画IK回调函数,Unity Documentation中这样介绍:OnAnimatorIK() 在即将更新其内部反向动力学系统前由动画器组件调用。该回调可用于设置反向动力学目标的位置及其各自的权重。
参数LayerIndex指的是Animator中的Layer层级的索引值。
如何设置IK目标的位置及其权重?需要用到Animator中的函数:
SetIKPosition
:设置一个IK Goal的位置SetIKPositionWeight
:设置IK Goal的过渡权重(0表示IK之前的原始动画,1表示在goal)SetIKRotation
:设置一个IK Goal的旋转SetIKRotationWeight
:设置IK Goal的旋转权重
Calculate IK Position & Rotation
如何获取IK目标位置及旋转?可以通过在脚部加上一定单位的高度上向下进行Raycast
射线检测,RaycastHit
中的point碰撞点即是IK的目标位置,并且通过normal法线方向获得IK的目标旋转,代码如下所示:
#region 计算左脚IK
//左脚坐标
leftFootPosition = animator.GetBoneTransform(HumanBodyBones.LeftFoot).position;
leftFootPosition.y = transform.position.y + raycastOriginHeight;//左脚 射线检测
leftFootRaycast = Physics.Raycast(leftFootPosition, Vector3.down, out RaycastHit hit, raycastDistance + raycastOriginHeight, layerMask);
if (leftFootRaycast)
{leftFootIkPosition = leftFootPosition;leftFootIkPosition.y = hit.point.y + bodyYOffset;leftFootIkRotation = Quaternion.FromToRotation(transform.up, hit.normal);
#if UNITY_EDITOR//射线Debug.DrawLine(leftFootPosition, leftFootPosition + Vector3.down * (raycastDistance + raycastOriginHeight), Color.yellow);//法线Debug.DrawLine(hit.point, hit.point + hit.normal * .5f, Color.cyan);
#endif
}
else
{leftFootIkPosition = Vector3.zero;
}
#endregion
Body Position
在设置IK目标位置之前,需要先计算和调整身体的高度,原因如下图所示,当射线检测到的IK Position,腿的长度达不到时,需要将身体的Y坐标减去相应距离。
身体高度通过Animator中的bodyPosition去调整:
代码如下所示:
#region 身体
if (leftFootRaycast && rightFootRaycast)
{//左脚坐标Y差值float leftPosYDelta = leftFootIkPosition.y - transform.position.y;//右脚坐标Y差值float rightPosYDelta = rightFootIkPosition.y - transform.position.y;//身体坐标Y差值取二者最小值float bodyPosYDelta = Mathf.Min(leftPosYDelta, rightPosYDelta);//目标身体坐标Vector3 targetBodyPosition = animator.bodyPosition + Vector3.up * bodyPosYDelta;//插值运算targetBodyPosition.y = Mathf.Lerp(lastBodyPositionY, targetBodyPosition.y, bodyPositionLerpSpeed);//设置身体坐标animator.bodyPosition = targetBodyPosition;
}
//缓存身体Y坐标
lastBodyPositionY = animator.bodyPosition.y;
#endregion
Apply IK Position & Rotation
求得目标位置和旋转并调整完身体高度后,应用目标位置和旋转即可:
#region 应用左脚IK
//权重
animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 1f);
animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, 1f);Vector3 targetIkPosition = animator.GetIKPosition(AvatarIKGoal.LeftFoot);
if (leftFootRaycast)
{//转局部坐标targetIkPosition = transform.InverseTransformPoint(targetIkPosition);Vector3 world2Local = transform.InverseTransformPoint(leftFootIkPosition);//插值计算float y = Mathf.Lerp(lastLeftFootPositionY, world2Local.y, footPositionLerpSpeed);targetIkPosition.y += y;lastLeftFootPositionY = y;//转全局坐标targetIkPosition = transform.TransformPoint(targetIkPosition);//当前旋转Quaternion currRotation = animator.GetIKRotation(AvatarIKGoal.LeftFoot);//目标旋转Quaternion nextRotation = leftFootIkRotation * currRotation;animator.SetIKRotation(AvatarIKGoal.LeftFoot, nextRotation);
}
animator.SetIKPosition(AvatarIKGoal.LeftFoot, targetIkPosition);
#endregion
这篇关于Unity Avatar Foot IK - Avatar Foot Placement Resolution的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!