Last active
January 20, 2022 18:08
-
-
Save JoernSchoenyan/06d2a371bdc81161a88b0bb71a29355b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've used three YT videos as reference and inspiration:
https://www.youtube.com/watch?v=rGB1ipH6DrM
https://www.youtube.com/watch?v=MonxKdgxi2w
https://www.youtube.com/watch?v=EggUxC5_lGE
Thank you!