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();
}
}
}