using UnityEngine; namespace CW.Common { /// This makes the current Transform follow the Target Transform as if it were a child. [ExecuteInEditMode] [HelpURL(CwShared.HelpUrlPrefix + "CwFollow")] [AddComponentMenu(CwShared.ComponentMenuPrefix + "Follow")] public class CwFollow : MonoBehaviour { public enum FollowType { TargetTransform, MainCamera } public enum UpdateType { Update, LateUpdate } /// What should this component follow? public FollowType Follow { set { follow = value; } get { return follow; } } [SerializeField] private FollowType follow; /// The transform that will be followed. public Transform Target { set { target = value; } get { return target; } } [SerializeField] private Transform target; /// How quickly this Transform follows the target. /// -1 = instant. public float Damping { set { damping = value; } get { return damping; } } [SerializeField] private float damping = -1.0f; /// Follow the target's rotation too? public bool Rotate { set { rotate = value; } get { return rotate; } } [SerializeField] private bool rotate = true; /// Ignore Z axis for 2D? public bool IgnoreZ { set { ignoreZ = value; } get { return ignoreZ; } } [SerializeField] private bool ignoreZ; /// Where in the game loop should this component update? public UpdateType FollowIn { set { followIn = value; } get { return followIn; } } [SerializeField] private UpdateType followIn = UpdateType.LateUpdate; /// This allows you to specify a positional offset relative to the Target. public Vector3 LocalPosition { set { localPosition = value; } get { return localPosition; } } [SerializeField] private Vector3 localPosition; /// This allows you to specify a rotational offset relative to the Target. public Vector3 LocalRotation { set { localRotation = value; } get { return localRotation; } } [SerializeField] private Vector3 localRotation; /// This method will update the follow position now. [ContextMenu("UpdatePosition")] public void UpdatePosition() { var finalTarget = target; if (follow == FollowType.MainCamera) { var mainCamera = Camera.main; if (mainCamera != null) { finalTarget = mainCamera.transform; } } if (finalTarget != null) { var currentPosition = transform.position; var targetPosition = finalTarget.TransformPoint(localPosition); var factor = CwHelper.DampenFactor(damping, Time.deltaTime); if (ignoreZ == true) { targetPosition.z = currentPosition.z; } transform.position = Vector3.Lerp(currentPosition, targetPosition, factor); if (rotate == true) { var targetRotation = finalTarget.rotation * Quaternion.Euler(localRotation); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, factor); } } } protected virtual void Update() { if (followIn == UpdateType.Update) { UpdatePosition(); } } protected virtual void LateUpdate() { if (followIn == UpdateType.LateUpdate) { UpdatePosition(); } } } } #if UNITY_EDITOR namespace CW.Common { using UnityEditor; using TARGET = CwFollow; [CanEditMultipleObjects] [CustomEditor(typeof(TARGET))] public class CwFollow_Editor : CwEditor { protected override void OnInspector() { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); Draw("follow", "What should this component follow?"); if (Any(tgts, t => t.Follow == CwFollow.FollowType.TargetTransform)) { BeginIndent(); BeginError(Any(tgts, t => t.Target == null)); Draw("target", "The transform that will be followed."); EndError(); EndIndent(); } Draw("damping", "How quickly this Transform follows the target.\n\n-1 = instant."); Draw("rotate", "Follow the target's rotation too?"); Draw("ignoreZ", "Ignore Z axis for 2D?"); Draw("followIn", "Where in the game loop should this component update?"); Separator(); Draw("localPosition", "This allows you to specify a positional offset relative to the Target transform."); Draw("localRotation", "This allows you to specify a rotational offset relative to the Target transform."); } } } #endif