Unity贴地行走(反向动力学)
2023/4/13 10:45:17
关键词:Unity 真实行走 贴地行走
2023/4/13 10:45:17
关键词:Unity 真实行走 贴地行走
Unity3D实现脚部IK-脚部贴合地形变化(反向动力学)
目标:实现脚部IK和头部IK,双脚的位置可以根据地形进行调整,可以使得头部朝向指定方向。
实现效果:首先要求模型是Humanoid类型(人形),才能调用Unity自带的AnimatorIK回调函数
主要的API:
头部:
m_animator.SetLookAtPosition(eyesIKTarget.position);// 注视的目标
m_animator.SetLookAtWeight(1f/*注视的全局权重*/, 0/*body的权重*/, 1/*头部权重*/, 1/*眼睛权重*/, 0.5f/*0-1,0为完全无约束,1是完全不能*/);//设置头部权重
脚部位置:
m_animator.SetIKPosition(AvatarIKGoal.LeftFoot/*关节位置,有4个选择*/, pointLeft);//设置脚的位置
m_animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 1f);//设置脚部的位置变化权重
脚部旋转:
m_animator.SetIKRotation(AvatarIKGoal.RightFoot, Quaternion.Euler(to));
m_animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, 1f);
手部同理。
代码实现:
原文链接:https://blog.csdn.net/cycler_725/article/details/120295186 [1] [2]
相关文章(向右看)..
目标:实现脚部IK和头部IK,双脚的位置可以根据地形进行调整,可以使得头部朝向指定方向。
实现效果:首先要求模型是Humanoid类型(人形),才能调用Unity自带的AnimatorIK回调函数
主要的API:
头部:
m_animator.SetLookAtPosition(eyesIKTarget.position);// 注视的目标
m_animator.SetLookAtWeight(1f/*注视的全局权重*/, 0/*body的权重*/, 1/*头部权重*/, 1/*眼睛权重*/, 0.5f/*0-1,0为完全无约束,1是完全不能*/);//设置头部权重
脚部位置:
m_animator.SetIKPosition(AvatarIKGoal.LeftFoot/*关节位置,有4个选择*/, pointLeft);//设置脚的位置
m_animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 1f);//设置脚部的位置变化权重
脚部旋转:
m_animator.SetIKRotation(AvatarIKGoal.RightFoot, Quaternion.Euler(to));
m_animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, 1f);
手部同理。
代码实现:
//控制人物的IK,仅当使用人形骨架时才会执行此事件,勾选了IK Pass的layer才会调用到这个方法里,每个勾选了IK Pass的layer调用一次
private void OnAnimatorIK(int layerIndex)
{
//头部IK
if (eyesIKTarget)
{
m_animator.SetLookAtPosition(eyesIKTarget.position);//设置看向的目标点位置
m_animator.SetLookAtWeight(1f/*注视的全局权重*/, 0/*body的权重*/, 1/*头部权重*/, 1/*眼睛权重*/, 0.5f/*0-1,0为完全无约束,1是完全不能*/);//设置头部权重
}
if (!IsGround)
return;
AnimatorStateInfo animInfo = m_animator.GetCurrentAnimatorStateInfo(0);
if(animInfo.IsTag("Idle") || animInfo.IsTag("DefaultIdleState") || (animInfo.IsTag("MotionTree") && m_forwardSpeed <= 1f)){ }//可以IK变化的状态
else
{
if (m_heightDelta != 0f)
{
m_heightDelta = 0f;
m_CharaCtrl.center = new Vector3(0, m_heightOrigin, 0);//重置人物重心
}
return;
}
float heightDeltaLeft = 0f, heightDeltaRight = 0f;
Vector3 pointLeft = leftFootIKTarget.position, pointRight = rightFootIKTarget.position;/**/
//设置IK位置:
if (leftFootIKTarget)
{
Ray r = new Ray(leftFootIKTarget.position + Vector3.up * 0.3f, -Vector3.up);
RaycastHit Info1,Info2;
int layerMask = (1 << 9); //Player层
layerMask = ~layerMask; //只过滤Player层
Debug.DrawRay(leftFootIKTarget.position + Vector3.up * 0.3f, -Vector3.up, Color.green);//可视化射线,scene视图中可以打开gizmo
if (Physics.Raycast(r, out Info1, 0.7f, layerMask, QueryTriggerInteraction.Ignore))
{
heightDeltaLeft = (leftFootIKTarget.position.y - Info1.point.y);
pointLeft = Info1.point;
pointLeft.y += 0.08f; //IK有一个偏差值,不加容易会穿模(可能是我这个模型问题)
//控制Ik角度:
r.origin = leftFootIKTarget_assist.position + Vector3.up * 0.3f;
if(Physics.Raycast(r , out Info2 , 0.7f,layerMask,QueryTriggerInteraction.Ignore))
{
//print("assist点" + Info2.point +"原点"+ Info1.point);
float slopeAngle = Mathf.Atan2(Info1.point.y - Info2.point.y,Vector3.Distance(Info1.point,Info2.point));//脚的旋转角度增量
//print("左脚坡度 " + slopeAngle);
Vector3 to = leftFootIKTarget.rotation.eulerAngles; //原始旋转值
to.x += slopeAngle * Mathf.Rad2Deg; //绕x轴旋转的角度 + slopAngle
m_animator.SetIKRotation(AvatarIKGoal.LeftFoot, Quaternion.Euler(to)); //设置脚的旋转
m_animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, 1f); //脚的旋转权重
}
}
}
if (rightFootIKTarget)
{
Ray r = new Ray(rightFootIKTarget.position + Vector3.up * 0.3f, -Vector3.up);
RaycastHit Info1,Info2;
int layerMask = (1 << 9); //打开Player层
layerMask = ~layerMask; //只过滤Player层
Debug.DrawRay(rightFootIKTarget.position + Vector3.up * 0.3f, -Vector3.up, Color.red);//可视化射线
if (Physics.Raycast(r, out Info1, 0.7f, layerMask, QueryTriggerInteraction.Ignore))
{
heightDeltaRight = (rightFootIKTarget.position.y - Info1.point.y);
pointRight = Info1.point;
pointRight.y += 0.08f; //加一个参数比较好,因为目标的point位置可能不够准确
//控制右脚旋转
r.origin = rightFootIKTarget_assist.position + Vector3.up * 0.3f;
if (Physics.Raycast(r, out Info2, 0.7f, layerMask, QueryTriggerInteraction.Ignore))
{
float slopeAngle = Mathf.Atan2(Info1.point.y - Info2.point.y, Vector3.Distance(Info1.point, Info2.point))/*返回的是弧度*/;
print("右脚坡度 " + slopeAngle);
Vector3 to = rightFootIKTarget.rotation.eulerAngles;
to.x += slopeAngle * Mathf.Rad2Deg;//绕x轴旋转的角度 + slopAngle
m_animator.SetIKRotation(AvatarIKGoal.RightFoot, Quaternion.Euler(to));
m_animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, 1f);
}
}
}
if (Mathf.Abs(heightDeltaLeft - heightDeltaRight) < 0.05f)//左右较为平坦,不降低重心
{
//print("较为平坦"+ "左差:" + heightDeltaLeft + "右差:" + heightDeltaRight);
//print("高度差:" + m_heightDelta);
m_CharaCtrl.center = new Vector3(0, m_heightOrigin, 0);//有待完善,还是会陷进去地面。。
m_animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 0f);
m_animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, 0f);
return;
}
//地形不平坦:
//降低重心(character高度不变下,使center提高)
m_animator.SetIKPosition(AvatarIKGoal.LeftFoot, pointLeft);//不只是这个位置,还不够(就离谱)
m_animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 1f);
m_animator.SetIKPosition(AvatarIKGoal.RightFoot, pointRight);
m_animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, 1f);
m_heightDelta = Mathf.Lerp(m_heightDelta, Math.Max(heightDeltaRight, heightDeltaLeft), Time.deltaTime);
m_CharaCtrl.center = new Vector3(0, m_heightOrigin + m_heightDelta*1.8f, 0);//有待完善,还是会有时候不够就悬空。。
}
原文链接:https://blog.csdn.net/cycler_725/article/details/120295186
相关文章(向右看)..