// Rigidbody Follow|Utilities|90120
namespace VRTK
{
using UnityEngine;
///
/// Changes one GameObject's rigidbody to follow another GameObject's rigidbody.
///
[AddComponentMenu("VRTK/Scripts/Utilities/Object Follow/VRTK_RigidbodyFollow")]
public class VRTK_RigidbodyFollow : VRTK_ObjectFollow
{
///
/// Specifies how to position and rotate the rigidbody.
///
public enum MovementOption
{
///
/// Use Rigidbody.position and Rigidbody.rotation.
///
Set,
///
/// Use Rigidbody.MovePosition and Rigidbody.MoveRotation.
///
Move,
///
/// Use Rigidbody.AddForce(Vector3) and Rigidbody.AddTorque(Vector3).
///
Add,
///
/// Use velocity and angular velocity with MoveTowards.
///
Track
}
[Header("Follow Settings")]
[Tooltip("Specifies how to position and rotate the rigidbody.")]
public MovementOption movementOption = MovementOption.Set;
[Header("Track Movement Settings")]
[Tooltip("The maximum distance the tracked `Game Object To Change` Rigidbody can be from the `Game Object To Follow` Rigidbody before the position is forcibly set to match the position.")]
public float trackMaxDistance = 0.25f;
protected Rigidbody rigidbodyToFollow;
protected Rigidbody rigidbodyToChange;
protected float maxDistanceDelta = 10f;
///
/// Follow `gameObjectToFollow` using the current settings.
///
public override void Follow()
{
CacheRigidbodies();
base.Follow();
}
protected virtual void OnDisable()
{
rigidbodyToFollow = null;
rigidbodyToChange = null;
}
protected virtual void FixedUpdate()
{
Follow();
}
protected virtual void CacheRigidbodies()
{
if (gameObjectToFollow == null || gameObjectToChange == null || (rigidbodyToFollow != null && rigidbodyToChange != null))
{
return;
}
rigidbodyToFollow = gameObjectToFollow.GetComponent();
rigidbodyToChange = gameObjectToChange.GetComponent();
}
protected override Vector3 GetPositionToFollow()
{
return (rigidbodyToFollow != null ? rigidbodyToFollow.position : Vector3.zero);
}
protected override Quaternion GetRotationToFollow()
{
return (rigidbodyToFollow != null ? rigidbodyToFollow.rotation : Quaternion.identity);
}
protected override Vector3 GetScaleToFollow()
{
return (rigidbodyToFollow != null ? rigidbodyToFollow.transform.localScale : Vector3.zero);
}
protected override void SetPositionOnGameObject(Vector3 newPosition)
{
switch (movementOption)
{
case MovementOption.Set:
rigidbodyToChange.position = newPosition;
break;
case MovementOption.Move:
rigidbodyToChange.MovePosition(newPosition);
break;
case MovementOption.Add:
// TODO: Test if this is correct
rigidbodyToChange.AddForce(newPosition - rigidbodyToChange.position);
break;
case MovementOption.Track:
TrackPosition(newPosition);
break;
}
}
protected override void SetRotationOnGameObject(Quaternion newRotation)
{
switch (movementOption)
{
case MovementOption.Set:
rigidbodyToChange.rotation = newRotation;
break;
case MovementOption.Move:
rigidbodyToChange.MoveRotation(newRotation);
break;
case MovementOption.Add:
// TODO: Test if this is correct
rigidbodyToChange.AddTorque(newRotation * Quaternion.Inverse(rigidbodyToChange.rotation).eulerAngles);
break;
case MovementOption.Track:
TrackRotation(newRotation);
break;
}
}
protected virtual void TrackPosition(Vector3 newPosition)
{
if (rigidbodyToFollow == null)
{
return;
}
if (Vector3.Distance(rigidbodyToChange.position, rigidbodyToFollow.position) > trackMaxDistance)
{
rigidbodyToChange.position = rigidbodyToFollow.position;
rigidbodyToChange.rotation = rigidbodyToFollow.rotation;
}
float trackVelocityLimit = float.PositiveInfinity;
Vector3 positionDelta = newPosition - rigidbodyToChange.position;
Vector3 velocityTarget = positionDelta / Time.fixedDeltaTime;
Vector3 calculatedVelocity = Vector3.MoveTowards(rigidbodyToChange.velocity, velocityTarget, maxDistanceDelta);
if (trackVelocityLimit == float.PositiveInfinity || calculatedVelocity.sqrMagnitude < trackVelocityLimit)
{
rigidbodyToChange.velocity = calculatedVelocity;
}
}
protected virtual void TrackRotation(Quaternion newRotation)
{
if (rigidbodyToFollow == null)
{
return;
}
float trackAngularVelocityLimit = float.PositiveInfinity;
Quaternion rotationDelta = newRotation * Quaternion.Inverse(rigidbodyToChange.rotation);
float angle;
Vector3 axis;
rotationDelta.ToAngleAxis(out angle, out axis);
angle = ((angle > 180) ? angle -= 360 : angle);
if (angle != 0)
{
Vector3 angularTarget = angle * axis;
Vector3 calculatedAngularVelocity = Vector3.MoveTowards(rigidbodyToChange.angularVelocity, angularTarget, maxDistanceDelta);
if (trackAngularVelocityLimit == float.PositiveInfinity || calculatedAngularVelocity.sqrMagnitude < trackAngularVelocityLimit)
{
rigidbodyToChange.angularVelocity = calculatedAngularVelocity;
}
}
}
}
}