using UnityEngine;
using System.Collections;
namespace com.rfilkov.components
{
///
/// HmdHeadMover moves the avatar model, according to the camera position reported by the HMD tracker.
/// Don't forget to enable the 'External root motion'-setting of the AvatarController-component in this case.
///
public class HmdHeadMover : MonoBehaviour
{
[Tooltip("The transform that needs to be followed by the avatar's head, usually the eye-camera position reported by the HMD tracker. When left empty, it defaults to the main camera's position.")]
public Transform targetTransform;
[Tooltip("The transform of the avatar's head. When left empty, it defaults to the head position, as reported by Animator-component.")]
private Transform headTransform;
[Tooltip("Whether the avatar's feet must stick to the ground.")]
public bool groundedFeet = false;
[Tooltip("The transform of the avatar's left toes, if grounding is enabled.")]
private Transform leftToes;
[Tooltip("The transform of the avatar's right toes, if grounding is enabled.")]
private Transform rightToes;
// grounder constants and variables
//private const int raycastLayers = ~2; // Ignore Raycast
private const float maxFootDistanceGround = 0.02f; // maximum distance from lower foot to the ground
private const float maxFootDistanceTime = 0.02f; // 0.2f; // maximum allowed time, the lower foot to be distant from the ground
//private Transform leftFoot, rightFoot;
private float fFootDistanceInitial = 0f;
private float fFootDistance = 0f;
private float fFootDistanceTime = 0f;
void Start()
{
// if the target transform is not set, use the camera transform
if (targetTransform == null && Camera.main != null)
{
targetTransform = Camera.main.transform;
}
}
void LateUpdate()
{
// move the head and body to the target
MoveHeadToTarget();
}
// moves the avatar's head to the target, and the rest of its body too
private void MoveHeadToTarget()
{
if (headTransform == null)
{
Animator animatorComponent = GetComponent();
headTransform = animatorComponent ? animatorComponent.GetBoneTransform(HumanBodyBones.Head) : null;
}
if (!targetTransform || !headTransform)
return;
Transform trans = headTransform.transform;
Vector3 posTrans = targetTransform.position;
while (trans.parent != null)
{
Transform transParent = trans.parent;
Vector3 dirParent = transParent.position - trans.position;
posTrans += dirParent;
trans = transParent;
}
if (groundedFeet)
{
// keep the current correction
float fLastTgtY = posTrans.y;
posTrans.y += fFootDistance;
float fNewDistance = GetDistanceToGround();
float fNewDistanceTime = Time.time;
// Debug.Log(string.Format("PosY: {0:F2}, LastY: {1:F2}, TgrY: {2:F2}, NewDist: {3:F2}, Corr: {4:F2}, Time: {5:F2}", bodyRoot != null ? bodyRoot.position.y : transform.position.y,
// fLastTgtY, targetPos.y, fNewDistance, fFootDistance, fNewDistanceTime));
if (Mathf.Abs(fNewDistance) >= 0.01f && Mathf.Abs(fNewDistance - fFootDistanceInitial) >= maxFootDistanceGround)
{
if ((fNewDistanceTime - fFootDistanceTime) >= maxFootDistanceTime)
{
fFootDistance += (fNewDistance - fFootDistanceInitial);
fFootDistanceTime = fNewDistanceTime;
posTrans.y = fLastTgtY + fFootDistance;
// Debug.Log(string.Format(" >> change({0:F2})! - Corr: {1:F2}, LastY: {2:F2}, TgrY: {3:F2} at time {4:F2}",
// (fNewDistance - fFootDistanceInitial), fFootDistance, fLastTgtY, targetPos.y, fFootDistanceTime));
}
}
else
{
fFootDistanceTime = fNewDistanceTime;
}
}
// set root transform position
if (trans)
{
trans.position = posTrans;
}
// Vector3 posDiff = targetTransform.position - headTransform.position;
// transform.position += posDiff;
//Debug.Log("PosTrans: " + posTrans + ", Transofrm: " + transform.position);
}
// returns the lower distance distance from left or right foot to the ground, or 1000f if no LF/RF transforms are found
private float GetDistanceToGround()
{
if (leftToes == null && rightToes == null)
{
Animator animatorComponent = GetComponent();
if (animatorComponent)
{
leftToes = animatorComponent.GetBoneTransform(HumanBodyBones.LeftToes);
rightToes = animatorComponent.GetBoneTransform(HumanBodyBones.RightToes);
}
}
float fDistMin = 1000f;
float fDistLeft = leftToes ? GetTransformDistanceToGround(leftToes) : fDistMin;
float fDistRight = rightToes ? GetTransformDistanceToGround(rightToes) : fDistMin;
fDistMin = Mathf.Abs(fDistLeft) < Mathf.Abs(fDistRight) ? fDistLeft : fDistRight;
if (fDistMin == 1000f)
{
fDistMin = 0f; // fFootDistanceInitial;
}
// Debug.Log (string.Format ("LFootY: {0:F2}, Dist: {1:F2}, RFootY: {2:F2}, Dist: {3:F2}, Min: {4:F2}", leftToes ? leftToes.position.y : 0f, fDistLeft,
// rightToes ? rightToes.position.y : 0f, fDistRight, fDistMin));
return fDistMin;
}
// returns distance from the given transform to the underlying object.
private float GetTransformDistanceToGround(Transform trans)
{
if (!trans)
return 0f;
// RaycastHit hit;
// if(Physics.Raycast(trans.position, Vector3.down, out hit, 2f, raycastLayers))
// {
// return -hit.distance;
// }
// else if(Physics.Raycast(trans.position, Vector3.up, out hit, 2f, raycastLayers))
// {
// return hit.distance;
// }
// else
// {
// if (trans.position.y < 0)
// return -trans.position.y;
// else
// return 1000f;
// }
return -trans.position.y;
}
}
}