// Object Follow|Utilities|90110
namespace VRTK
{
using UnityEngine;
///
/// Abstract class that allows to change one game object's properties to follow another game object.
///
public abstract class VRTK_ObjectFollow : MonoBehaviour
{
[Header("Object Settings")]
[Tooltip("The game object to follow. The followed property values will be taken from this one.")]
public GameObject gameObjectToFollow;
[Tooltip("The game object to change the property values of. If left empty the game object this script is attached to will be changed.")]
public GameObject gameObjectToChange;
[Header("Position Settings")]
[Tooltip("Whether to follow the position of the given game object.")]
public bool followsPosition = true;
[Tooltip("Whether to smooth the position when following `gameObjectToFollow`.")]
public bool smoothsPosition;
[Tooltip("The maximum allowed distance between the unsmoothed source and the smoothed target per frame to use for smoothing.")]
public float maxAllowedPerFrameDistanceDifference = 0.003f;
///
/// The position that results by following `gameObjectToFollow`.
///
public Vector3 targetPosition { get; private set; }
[Header("Rotation Settings")]
[Tooltip("Whether to follow the rotation of the given game object.")]
public bool followsRotation = true;
[Tooltip("Whether to smooth the rotation when following `gameObjectToFollow`.")]
public bool smoothsRotation;
[Tooltip("The maximum allowed angle between the unsmoothed source and the smoothed target per frame to use for smoothing.")]
public float maxAllowedPerFrameAngleDifference = 1.5f;
///
/// The rotation that results by following `gameObjectToFollow`.
///
public Quaternion targetRotation { get; private set; }
[Header("Scale Settings")]
[Tooltip("Whether to follow the scale of the given game object.")]
public bool followsScale = true;
[Tooltip("Whether to smooth the scale when following `gameObjectToFollow`.")]
public bool smoothsScale;
[Tooltip("The maximum allowed size between the unsmoothed source and the smoothed target per frame to use for smoothing.")]
public float maxAllowedPerFrameSizeDifference = 0.003f;
///
/// The scale that results by following `gameObjectToFollow`.
///
public Vector3 targetScale { get; private set; }
///
/// Follow `gameObjectToFollow` using the current settings.
///
public virtual void Follow()
{
if (gameObjectToFollow == null)
{
return;
}
if (followsPosition)
{
FollowPosition();
}
if (followsRotation)
{
FollowRotation();
}
if (followsScale)
{
FollowScale();
}
}
protected virtual void OnEnable()
{
gameObjectToChange = gameObjectToChange != null ? gameObjectToChange : gameObject;
}
protected virtual void OnValidate()
{
maxAllowedPerFrameDistanceDifference = Mathf.Max(0.0001f, maxAllowedPerFrameDistanceDifference);
maxAllowedPerFrameAngleDifference = Mathf.Max(0.0001f, maxAllowedPerFrameAngleDifference);
maxAllowedPerFrameSizeDifference = Mathf.Max(0.0001f, maxAllowedPerFrameSizeDifference);
}
protected abstract Vector3 GetPositionToFollow();
protected abstract void SetPositionOnGameObject(Vector3 newPosition);
protected abstract Quaternion GetRotationToFollow();
protected abstract void SetRotationOnGameObject(Quaternion newRotation);
protected virtual Vector3 GetScaleToFollow()
{
return gameObjectToFollow.transform.localScale;
}
protected virtual void SetScaleOnGameObject(Vector3 newScale)
{
gameObjectToChange.transform.localScale = newScale;
}
protected virtual void FollowPosition()
{
Vector3 positionToFollow = GetPositionToFollow();
Vector3 newPosition;
if (smoothsPosition)
{
float alpha = Mathf.Clamp01(Vector3.Distance(targetPosition, positionToFollow) / maxAllowedPerFrameDistanceDifference);
newPosition = Vector3.Lerp(targetPosition, positionToFollow, alpha);
}
else
{
newPosition = positionToFollow;
}
targetPosition = newPosition;
SetPositionOnGameObject(newPosition);
}
protected virtual void FollowRotation()
{
Quaternion rotationToFollow = GetRotationToFollow();
Quaternion newRotation;
if (smoothsRotation)
{
float alpha = Mathf.Clamp01(Quaternion.Angle(targetRotation, rotationToFollow) / maxAllowedPerFrameAngleDifference);
newRotation = Quaternion.Lerp(targetRotation, rotationToFollow, alpha);
}
else
{
newRotation = rotationToFollow;
}
targetRotation = newRotation;
SetRotationOnGameObject(newRotation);
}
protected virtual void FollowScale()
{
Vector3 scaleToFollow = GetScaleToFollow();
Vector3 newScale;
if (smoothsScale)
{
float alpha = Mathf.Clamp01(Vector3.Distance(targetScale, scaleToFollow) / maxAllowedPerFrameSizeDifference);
newScale = Vector3.Lerp(targetScale, scaleToFollow, alpha);
}
else
{
newScale = scaleToFollow;
}
targetScale = newScale;
SetScaleOnGameObject(newScale);
}
}
}