Skip to content

Instantly share code, notes, and snippets.

@JoernSchoenyan
Last active January 20, 2022 18:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoernSchoenyan/06d2a371bdc81161a88b0bb71a29355b to your computer and use it in GitHub Desktop.
Save JoernSchoenyan/06d2a371bdc81161a88b0bb71a29355b to your computer and use it in GitHub Desktop.
using UnityEngine;
public class FootIK : MonoBehaviour
{
[SerializeField] private float offsetY = 0.125f;
[SerializeField] private float heightFromGround = 0.5f;
[SerializeField] private float ikSpeed = 20.0f;
[SerializeField] private float pelvisSpeed = 5.0f;
[SerializeField] private string leftFootIKCurve = "LeftFootIKCurve";
[SerializeField] private string rightFootIKCurve = "RightFootIKCurve";
private Animator animator = null;
private Transform leftFoot, rightFoot;
private Vector3 leftFootPos, rightFootPos, lastLeftFootPos, lastRightFootPos;
private Quaternion leftFootRotation, rightFootRotation;
private float leftFootIKWeight, rightFootIKWeight;
private float lastPelvisPosY = Mathf.Infinity;
private void Awake()
{
animator = GetComponent<Animator>();
leftFoot = animator.GetBoneTransform(HumanBodyBones.LeftFoot);
rightFoot = animator.GetBoneTransform(HumanBodyBones.RightFoot);
}
private void Update(){
}
private void UpdateIK()
{
RaycastHit leftHit, rightHit;
Vector3 leftPos = leftFoot.TransformPoint(Vector3.zero);
Vector3 rightPos = rightFoot.TransformPoint(Vector3.zero);
if (Physics.Raycast(leftPos + Vector3.up * heightFromGround, Vector3.down, out leftHit, 1))
{
// if the raycasthit is HIGHER than the foot pos in the last frame, the new pos needs to be
// HIGHER NOW without interpolation - the foot should not vanish in a stone when the stone rises!
// if the raycasthit is LOWER the foot can get down over time
leftFootPos = leftHit.point;
if (leftHit.point.y < lastLeftFootPos.y)
{
leftFootPos.y = Mathf.Lerp(lastLeftFootPos.y, leftFootPos.y, Time.deltaTime * ikSpeed);
}
var rotation = Quaternion.FromToRotation(transform.up, leftHit.normal) * transform.rotation;
// getting the y rotation from the animation looks more natural
leftFootRotation = Quaternion.Euler(rotation.eulerAngles.x, leftFoot.eulerAngles.y, rotation.eulerAngles.z);
}
if (Physics.Raycast(rightPos + Vector3.up * heightFromGround, Vector3.down, out rightHit, 1))
{
rightFootPos = rightHit.point;
if (rightHit.point.y < lastRightFootPos.y)
{
rightFootPos.y = Mathf.Lerp(lastRightFootPos.y, rightFootPos.y, Time.deltaTime * ikSpeed);
}
var rotation = Quaternion.FromToRotation(transform.up, rightHit.normal) * transform.rotation;
rightFootRotation = Quaternion.Euler(rotation.eulerAngles.x, leftFoot.eulerAngles.y, rotation.eulerAngles.z);
}
lastLeftFootPos = leftFootPos;
lastRightFootPos = rightFootPos;
}
private void AdjustFeetPositionAndRotation()
{
leftFootIKWeight = animator.GetFloat(leftFootIKCurve);
rightFootIKWeight = animator.GetFloat(rightFootIKCurve);
animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, leftFootIKWeight);
animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, rightFootIKWeight);
animator.SetIKPosition(AvatarIKGoal.LeftFoot, leftFootPos + new Vector3(0, offsetY, 0));
animator.SetIKPosition(AvatarIKGoal.RightFoot, rightFootPos + new Vector3(0, offsetY, 0));
animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, leftFootIKWeight);
animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, leftFootIKWeight);
animator.SetIKRotation(AvatarIKGoal.LeftFoot, leftFootRotation);
animator.SetIKRotation(AvatarIKGoal.RightFoot, rightFootRotation);
}
private void AdjustPelvisHeight()
{
if (lastPelvisPosY == Mathf.Infinity)
{
lastPelvisPosY = animator.bodyPosition.y;
}
float leftOffsetPos = leftFootPos.y - transform.position.y;
float rightOffsetPos = rightFootPos.y - transform.position.y;
float totalOffset = (leftOffsetPos < rightOffsetPos) ? leftOffsetPos : rightOffsetPos;
Vector3 newPelvisPos = animator.bodyPosition + Vector3.up * totalOffset;
newPelvisPos.y = Mathf.Lerp(lastPelvisPosY, newPelvisPos.y, pelvisSpeed * Time.deltaTime);
animator.bodyPosition = newPelvisPos;
lastPelvisPosY = animator.bodyPosition.y;
}
private void OnAnimatorIK(int layerIndex)
{
UpdateIK();
AdjustFeetPositionAndRotation();
AdjustPelvisHeight();
}
}
@JoernSchoenyan
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment