using UnityEngine; using FSA = UnityEngine.Serialization.FormerlySerializedAsAttribute; namespace Lean.Common { /// This component keeps the current GameObject the specified distance away from its parent. [ExecuteInEditMode] [HelpURL(LeanHelper.PlusHelpUrlPrefix + "LeanMaintainDistance")] [AddComponentMenu(LeanHelper.ComponentPathPrefix + "Maintain Distance")] public class LeanMaintainDistance : MonoBehaviour { /// The direction of the distance separation. /// 0,0,0 = Use current direction. public Vector3 Direction { set { direction = value; } get { return direction; } } [FSA("Direction")] [SerializeField] private Vector3 direction; /// The coordinate space for the Direction values. public Space DirectionSpace { set { directionSpace = value; } get { return directionSpace; } } [FSA("DirectionSpace")] [SerializeField] private Space directionSpace = Space.Self; /// The distance we want to be from the parent in world space. public float Distance { set { distance = value; } get { return distance; } } [FSA("Distance")] [SerializeField] private float distance = 10.0f; /// If you want this component to change smoothly over time, then this allows you to control how quick the changes reach their target value. /// -1 = Instantly change. /// 1 = Slowly change. /// 10 = Quickly change. public float Damping { set { damping = value; } get { return damping; } } [FSA("Dampening")] [FSA("Damping")] [SerializeField] private float damping = 3.0f; /// Should the distance value be clamped? public bool Clamp { set { clamp = value; } get { return clamp; } } [FSA("DistanceClamp")] [FSA("Clamp")] [SerializeField] private bool clamp; /// The minimum distance. public float ClampMin { set { clampMin = value; } get { return clampMin; } } [FSA("DistanceMin")] [FSA("ClampMin")] [SerializeField] private float clampMin = 1.0f; /// The maximum distance. public float ClampMax { set { clampMax = value; } get { return clampMax; } } [FSA("DistanceMax")] [FSA("ClampMax")] [SerializeField] private float clampMax = 100.0f; /// The layers we should collide against. public LayerMask CollisionLayers { set { collisionLayers = value; } get { return collisionLayers; } } [FSA("CollisionLayers")] [SerializeField] private LayerMask collisionLayers; /// The radius of the collision. public float CollisionRadius { set { collisionRadius = value; } get { return collisionRadius; } } [FSA("CollisionRadius")] [SerializeField] private float collisionRadius = 0.1f; [SerializeField] private float currentDistance; /// This method allows you to increment the Distance value by the specified value. public void AddDistance(float value) { distance += value; } /// This method allows you to multiply the Distance value by the specified value. public void MultiplyDistance(float value) { distance *= value; } protected virtual void Start() { currentDistance = distance; } protected virtual void LateUpdate() { var worldOrigin = transform.parent != null ? transform.parent.position : Vector3.zero; var worldDirection = direction; // Get a valid normalized direction if (worldDirection.sqrMagnitude == 0.0f) { worldDirection = transform.position - worldOrigin; if (worldDirection.sqrMagnitude == 0.0f) { worldDirection = Random.onUnitSphere; } } else if (directionSpace == Space.Self) { worldDirection = transform.TransformDirection(worldDirection); } worldDirection = worldDirection.normalized; // Limit distance to min/max values? if (clamp == true) { distance = Mathf.Clamp(distance, clampMin, clampMax); } // Collide against stuff? if (collisionLayers != 0) { var hit = default(RaycastHit); var pointA = worldOrigin + worldDirection * clampMin; var pointB = worldOrigin + worldDirection * clampMax; if (Physics.SphereCast(pointA, collisionRadius, worldDirection, out hit, Vector3.Distance(pointA, pointB), collisionLayers) == true) { var newDistance = hit.distance + clampMin; // Only update if the distance is closer, else the camera can glue to walls behind it if (newDistance < distance) { distance = newDistance; } } } // Get t value var factor = LeanHelper.GetDampenFactor(damping, Time.deltaTime); // Lerp the current value to the target one currentDistance = Mathf.Lerp(currentDistance, distance, factor); // Set the position transform.position = worldOrigin + worldDirection * currentDistance; } } } #if UNITY_EDITOR namespace Lean.Common.Editor { using TARGET = LeanMaintainDistance; [UnityEditor.CanEditMultipleObjects] [UnityEditor.CustomEditor(typeof(TARGET), true)] public class LeanMaintainDistance_Editor : LeanEditor { protected override void OnInspector() { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); Draw("direction", "The direction of the distance separation.\n\n0,0,0 = Use current direction."); Draw("directionSpace", "The coordinate space for the Direction values."); Draw("distance", "The distance we want to be from the parent in world space."); Draw("damping", "If you want this component to change smoothly over time, then this allows you to control how quick the changes reach their target value.\n\n-1 = Instantly change.\n\n1 = Slowly change.\n\n10 = Quickly change."); Draw("clamp", "Should the distance value be clamped?"); Draw("clampMin", "The minimum distance."); Draw("clampMax", "The maximum distance."); Draw("collisionLayers", "The layers we should collide against."); Draw("collisionRadius", "The radius of the collision."); } } } #endif