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