namespace Zinnia.Tracking.Follow.Modifier.Property { using System.Collections.Generic; using UnityEngine; using Zinnia.Extension; /// /// Modifies a property by a mechanism that can cause the target to diverge from the source. /// public abstract class DivergablePropertyModifier : PropertyModifier { #region Divergence Events /// /// Emitted when the target is back within the threshold distance of the source after being diverged. /// [Header("Divergence Events")] public ObjectFollower.FollowEvent Converged = new ObjectFollower.FollowEvent(); /// /// Emitted when the target is no longer within the threshold distance of the source. /// public ObjectFollower.FollowEvent Diverged = new ObjectFollower.FollowEvent(); #endregion #region Divergence Settings [Header("Divergence Settings")] [Tooltip("Determines if to track whether the source diverges from the target. Tracking divergence adds additional overhead.")] [SerializeField] private bool trackDivergence; /// /// Determines if to track whether the source diverges from the target. Tracking divergence adds additional overhead. /// public bool TrackDivergence { get { return trackDivergence; } set { trackDivergence = value; } } [Tooltip("The distance the target has to be away from the source to be considered diverged.")] [SerializeField] private Vector3 divergenceThreshold = Vector3.one * 0.1f; /// /// The distance the target has to be away from the source to be considered diverged. /// public Vector3 DivergenceThreshold { get { return divergenceThreshold; } set { divergenceThreshold = value; } } #endregion /// /// A collection of currently diverged states. /// protected HashSet divergedStates = new HashSet(); /// /// Sets the x value. /// /// The value to set to. public virtual void SetDivergenceThresholdX(float value) { DivergenceThreshold = new Vector3(value, DivergenceThreshold.y, DivergenceThreshold.z); } /// /// Sets the y value. /// /// The value to set to. public virtual void SetDivergenceThresholdY(float value) { DivergenceThreshold = new Vector3(DivergenceThreshold.x, value, DivergenceThreshold.z); } /// /// Sets the z value. /// /// The value to set to. public virtual void SetDivergenceThresholdZ(float value) { DivergenceThreshold = new Vector3(DivergenceThreshold.x, DivergenceThreshold.y, value); } /// /// Manually remove a source and target from the diverged state. /// /// The source to diverge from. /// The target that has diverged. public virtual void RemoveFromDiverged(GameObject source, GameObject target) { divergedStates.Remove(GenerateIdentifier(source, target)); } /// /// Clears the diverged states of all diverging source/targets. /// public virtual void ClearDiverged() { divergedStates.Clear(); } /// /// Whether the given source and target are diverged. /// /// The source to check against. /// The target to check with. /// Whether a divergence is occurring. public virtual bool AreDiverged(GameObject source, GameObject target) { return divergedStates.Contains(GenerateIdentifier(source, target)); } /// /// Gets the two points from the source and target to check divergence against. /// /// The source to check against. /// The target to check with. /// Any offset applied to the target. /// The first point to use. /// The second point to use. protected abstract void GetCheckPoints(GameObject source, GameObject target, GameObject offset, out Vector3 a, out Vector3 b); /// protected override void DoModify(GameObject source, GameObject target, GameObject offset = null) { if (TrackDivergence) { CheckDivergence(source, target, offset); } } /// /// Generates the unique key for the divergence check. /// /// The source of the divergence check. /// The target of the divergence check. /// The unique identifier. protected virtual string GenerateIdentifier(GameObject source, GameObject target) { return source.GetInstanceID() + "-" + target.GetInstanceID(); } /// /// Checks to see if the target has diverged from the source within the . /// /// The source to check against. /// The target to check with. /// Any offset applied to the target. protected virtual void CheckDivergence(GameObject source, GameObject target, GameObject offset) { string divergeKey = GenerateIdentifier(source, target); bool areDiverged = AreDiverged(source, target); GetCheckPoints(source, target, offset, out Vector3 sourcePoint, out Vector3 targetPoint); if (!sourcePoint.WithinDistance(targetPoint, DivergenceThreshold)) { if (areDiverged) { return; } divergedStates.Add(divergeKey); Diverged?.Invoke(eventData.Set(source, target, offset)); } else if (areDiverged) { divergedStates.Remove(divergeKey); Converged?.Invoke(eventData.Set(source, target, offset)); } } } }