每日一句:人生最精彩的不是实现梦想的瞬间,而是坚持梦想的过程
目录
定义:
准备:
API:
设置IK
头部IK——设置人物的头部根据视角旋转
手脚IK
案例:脚步IK
一般来说,骨骼动画都是传统的从父节点到子节点的带动方式(即正向动力学),IK则倒过来,由骨骼子节点带动骨骼父节点。
根据骨骼的终节点来推算其他父节点的位置的一种方式。
比如人物走路踩到了石头,就需要由脚的子节点带动全身骨骼做出踩到石头的响应。
·Model的Aniamtion Type设置为Humanoid
·检测Avatar是否异常
·Animator勾选Ik Pass
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()函数调用中设置该值)
用到两个API
SetLookAtPosition
SertLookAtWeight用来设置IK的权重,IK会和原来的动画进行混合。如果权重为1,则完全用IK的位置和旋转,如果权重为0,则完全用原来动画中的位置和旋转
void OnAnimatorIK(int layerIndex)
{
ani.SetLookAtPosition(pos);
ani.SetLookAtWeight(1);
}
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);
}
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
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;
}
}