// Rigidbody Follow|Utilities|90120
|
|
namespace VRTK
|
|
{
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// Changes one GameObject's rigidbody to follow another GameObject's rigidbody.
|
|
/// </summary>
|
|
[AddComponentMenu("VRTK/Scripts/Utilities/Object Follow/VRTK_RigidbodyFollow")]
|
|
public class VRTK_RigidbodyFollow : VRTK_ObjectFollow
|
|
{
|
|
/// <summary>
|
|
/// Specifies how to position and rotate the rigidbody.
|
|
/// </summary>
|
|
public enum MovementOption
|
|
{
|
|
/// <summary>
|
|
/// Use Rigidbody.position and Rigidbody.rotation.
|
|
/// </summary>
|
|
Set,
|
|
/// <summary>
|
|
/// Use Rigidbody.MovePosition and Rigidbody.MoveRotation.
|
|
/// </summary>
|
|
Move,
|
|
/// <summary>
|
|
/// Use Rigidbody.AddForce(Vector3) and Rigidbody.AddTorque(Vector3).
|
|
/// </summary>
|
|
Add,
|
|
/// <summary>
|
|
/// Use velocity and angular velocity with MoveTowards.
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Follow `gameObjectToFollow` using the current settings.
|
|
/// </summary>
|
|
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<Rigidbody>();
|
|
rigidbodyToChange = gameObjectToChange.GetComponent<Rigidbody>();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|