namespace Zinnia.Tracking.Query { using UnityEngine; using UnityEngine.Events; using Zinnia.Extension; using Zinnia.Process; /// /// Determines whether a faces (through the local forward direction) another . /// /// /// No physics checks are done and as such occlusion isn't part of the information gained by this component. /// public class FacingQuery : MonoBehaviour, IProcessable { [Tooltip("The object used as the origin to check if it is facing towards Target.")] [SerializeField] private GameObject source; /// /// The object used as the origin to check if it is facing towards . /// public GameObject Source { get { return source; } set { source = value; } } [Tooltip("The object that will be checked to see if Source is facing it.")] [SerializeField] private GameObject target; /// /// The object that will be checked to see if is facing it. /// public GameObject Target { get { return target; } set { target = value; } } [Tooltip("A sphere radius that defines the volume in which Target can still be considered seen by the Source.")] [SerializeField] private float targetRadius = 0.1f; /// /// A sphere radius that defines the volume in which can still be considered seen by the . /// public float TargetRadius { get { return targetRadius; } set { targetRadius = value; } } /// /// Emitted when is facing . /// public UnityEvent TargetFaced = new UnityEvent(); /// /// Emitted when no longer faces . /// public UnityEvent TargetNotFaced = new UnityEvent(); /// /// Whether was previously facing . /// protected bool? wasPreviouslyFacing; /// /// Clears . /// public virtual void ClearSource() { if (!this.IsValidState()) { return; } Source = default; } /// /// Clears . /// public virtual void ClearTarget() { if (!this.IsValidState()) { return; } Target = default; } /// /// Determines whether is facing defined by its position and . /// public virtual void Process() { Vector3 sourcePosition = Source.transform.position; Vector3 targetPosition = Target.transform.position; float distance = Vector3.Distance(targetPosition, sourcePosition); Vector3 glancePoint = sourcePosition + (Source.transform.forward * distance); bool isFacing = Vector3.Distance(targetPosition, glancePoint) <= TargetRadius; if (isFacing == wasPreviouslyFacing) { return; } wasPreviouslyFacing = isFacing; if (isFacing) { TargetFaced?.Invoke(); } else { TargetNotFaced?.Invoke(); } } protected virtual void OnDisable() { wasPreviouslyFacing = null; } } }