// Object Follow|Utilities|90110
|
|
namespace VRTK
|
|
{
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// Abstract class that allows to change one game object's properties to follow another game object.
|
|
/// </summary>
|
|
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;
|
|
/// <summary>
|
|
/// The position that results by following `gameObjectToFollow`.
|
|
/// </summary>
|
|
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;
|
|
/// <summary>
|
|
/// The rotation that results by following `gameObjectToFollow`.
|
|
/// </summary>
|
|
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;
|
|
/// <summary>
|
|
/// The scale that results by following `gameObjectToFollow`.
|
|
/// </summary>
|
|
public Vector3 targetScale { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Follow `gameObjectToFollow` using the current settings.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|