namespace Zinnia.Tracking.Follow { using System; using UnityEngine; using UnityEngine.Events; using Zinnia.Extension; using Zinnia.Process; /// /// Compares the distance between two GameObjects and emits an event when a given threshold is exceeded or falls within it. /// /// /// If the and the are the same then the initial position of the is used as the position. /// public class ObjectDistanceComparator : MonoBehaviour, IProcessable { /// /// Holds data about a event. /// [Serializable] public class EventData { [Tooltip("The difference of the positions of the target and source.")] [SerializeField] private Vector3 currentDifference; /// /// The difference of the positions of the target and source. /// public Vector3 CurrentDifference { get { return currentDifference; } set { currentDifference = value; } } [Tooltip("The distance between the source and target.")] [SerializeField] private float currentDistance; /// /// The distance between the source and target. /// public float CurrentDistance { get { return currentDistance; } set { currentDistance = value; } } public EventData Set(EventData source) { return Set(source.CurrentDifference, source.CurrentDistance); } public EventData Set(Vector3 difference, float distance) { CurrentDifference = difference; CurrentDistance = distance; return this; } public void Clear() { Set(default, default); } } /// /// Defines the event with the . /// [Serializable] public class UnityEvent : UnityEvent { } [Tooltip("The source of the distance measurement.")] [SerializeField] private GameObject source; /// /// The source of the distance measurement. /// public GameObject Source { get { return source; } set { source = value; if (this.IsMemberChangeAllowed()) { OnAfterSourceChange(); } } } [Tooltip("The target of the distance measurement.")] [SerializeField] private GameObject target; /// /// The target of the distance measurement. /// public GameObject Target { get { return target; } set { target = value; if (this.IsMemberChangeAllowed()) { OnAfterTargetChange(); } } } [Tooltip("The distance between the source and target that is considered to be exceeding the given threshold.")] [SerializeField] private float distanceThreshold = 1f; /// /// The distance between the source and target that is considered to be exceeding the given threshold. /// public float DistanceThreshold { get { return distanceThreshold; } set { distanceThreshold = value; } } /// /// Emitted when the distance between the source and the target exceeds the threshold. /// public UnityEvent ThresholdExceeded = new UnityEvent(); /// /// Emitted when the distance between the source and the target falls back within the threshold. /// public UnityEvent ThresholdResumed = new UnityEvent(); /// /// The difference of the positions of the target and source. /// public Vector3 Difference { get; protected set; } /// /// The distance between the source and target. /// public float Distance { get; protected set; } /// /// Determines if the distance between the source and target is exceeding the threshold. /// public bool Exceeding { get; protected set; } /// /// The previous state of the distance threshold being exceeded. /// protected bool previousState; /// /// The current position of . /// protected Vector3 sourcePosition; /// /// The event data to emit. /// protected readonly EventData eventData = new EventData(); /// /// Clears . /// public virtual void ClearSource() { if (!this.IsValidState()) { return; } Source = default; } /// /// Clears . /// public virtual void ClearTarget() { if (!this.IsValidState()) { return; } Target = default; } /// /// Checks to see if the distance between the source and target exceed the threshold. /// public virtual void Process() { if (!this.IsValidState() || Source == null || Target == null) { return; } Difference = Target.transform.position - GetSourcePosition(); Distance = Difference.magnitude; Exceeding = Distance >= DistanceThreshold; bool didStateChange = previousState != Exceeding; previousState = Exceeding; if (!didStateChange && DistanceThreshold > 0f) { return; } eventData.Set(Difference, Distance); if (Exceeding) { ThresholdExceeded?.Invoke(eventData); } else { ThresholdResumed?.Invoke(eventData); } } /// /// Attempts to save the current position as the initial position if the and the are the same . /// public virtual void SavePosition() { if (Source == null || Source != Target) { return; } sourcePosition = Target.transform.position; previousState = false; } protected virtual void OnEnable() { SavePosition(); } /// /// Gets the actual position for the based on whether it's a different or whether it is set up to use the initial position of the . /// /// The appropriate position. protected virtual Vector3 GetSourcePosition() { if (Source == null) { return Vector3.zero; } return Source == Target ? sourcePosition : Source.transform.position; } /// /// Called after has been changed. /// protected virtual void OnAfterSourceChange() { SavePosition(); } /// /// Called after has been changed. /// protected virtual void OnAfterTargetChange() { SavePosition(); } } }