namespace Tilia.Interactions.Interactables.Interactables { using System; using System.Collections; using System.Collections.Generic; using Tilia.Interactions.Interactables.Interactables.Grab.Provider; using Tilia.Interactions.Interactables.Interactables.Grab.Receiver; using Tilia.Interactions.Interactables.Interactors; using UnityEngine; using UnityEngine.Events; using Zinnia.Data.Attribute; using Zinnia.Extension; /// /// The public interface into the Interactable Prefab. /// public class InteractableFacade : MonoBehaviour { /// /// Defines the event with the . /// [Serializable] public class UnityEvent : UnityEvent { } /// /// The allowed interaction states. /// [Flags] public enum InteractionTypes { /// /// Whether the Interactable can be touched. /// Touch = 1 << 0, /// /// Whether the Interactable can be primarily grabbed. /// PrimaryGrab = 1 << 1, /// /// Whether the Interactable can be secondarily grabbed. /// SecondaryGrab = 1 << 2, } #region Reference Settings [Header("Reference Settings")] [Tooltip("The linked InteractableConfigurator.")] [SerializeField] [Restricted] private InteractableConfigurator configuration; /// /// The linked . /// public InteractableConfigurator Configuration { get { return configuration; } set { configuration = value; } } [Tooltip("The types of interaction that are valid on the Interactable.")] [SerializeField] [UnityFlags] private InteractionTypes validInteractionTypes = (InteractionTypes)(-1); /// /// The types of interaction that are valid on the Interactable. /// public InteractionTypes ValidInteractionTypes { get { return validInteractionTypes; } set { validInteractionTypes = value; if (this.IsMemberChangeAllowed()) { OnAfterValidInteractionTypesChange(); } } } #endregion #region Touch Events /// /// Emitted when the Interactable is touched for the first time by an Interactor. /// [Header("Touch Events")] public UnityEvent FirstTouched = new UnityEvent(); /// /// Emitted when an Interactor touches the Interactable. /// public UnityEvent Touched = new UnityEvent(); /// /// Emitted when an Interactor stops touching the Interactable. /// public UnityEvent Untouched = new UnityEvent(); /// /// Emitted when the Interactable is untouched for the last time by an Interactor. /// public UnityEvent LastUntouched = new UnityEvent(); #endregion #region Grab Events /// /// Emitted when the Interactable is grabbed for the first time by an Interactor. /// [Header("Grab Events")] public UnityEvent FirstGrabbed = new UnityEvent(); /// /// Emitted when an Interactor grabs the Interactable. /// public UnityEvent Grabbed = new UnityEvent(); /// /// Emitted when an Interactor ungrabs the Interactable. /// public UnityEvent Ungrabbed = new UnityEvent(); /// /// Emitted when the Interactable is ungrabbed for the last time by an Interactor. /// public UnityEvent LastUngrabbed = new UnityEvent(); #endregion #region Grab Action Settings [Header("Grab Action Settings")] [Tooltip("The linked GrabInteractableReceiver.ActiveType.")] [SerializeField] private GrabInteractableReceiver.ActiveType grabType; /// /// The linked . /// public GrabInteractableReceiver.ActiveType GrabType { get { return grabType; } set { grabType = value; if (this.IsMemberChangeAllowed()) { OnAfterGrabTypeChange(); } } } [Tooltip("The GrabInteractableInteractorProvider to use.")] [SerializeField] private int grabProviderIndex; /// /// The to use. /// public int GrabProviderIndex { get { return grabProviderIndex; } set { grabProviderIndex = value; if (this.IsMemberChangeAllowed()) { OnAfterGrabProviderIndexChange(); } } } [Tooltip("Attempts to swap the Secondary action to become the primary action if the primary interactor ungrabs causing a force ungrab of the secondary interactor.")] [SerializeField] private bool attemptSwapSecondaryForPrimaryOnPrimaryDrop; /// /// Attempts to swap the Secondary action to become the primary action if the primary interactor ungrabs causing a force ungrab of the secondary interactor. /// public bool AttemptSwapSecondaryForPrimaryOnPrimaryDrop { get { return attemptSwapSecondaryForPrimaryOnPrimaryDrop; } set { attemptSwapSecondaryForPrimaryOnPrimaryDrop = value; if (this.IsMemberChangeAllowed()) { OnAfterAttemptSwapSecondaryForPrimaryOnPrimaryDropChange(); } } } #endregion /// /// The attached to the Interactable. /// public virtual Rigidbody InteractableRigidbody => Configuration.ConsumerRigidbody; /// /// The mesh container. /// public virtual GameObject MeshContainer => Configuration.MeshContainer; /// /// Returns a collection of all the child meshes. /// public virtual MeshRenderer[] Meshes => MeshContainer.GetComponentsInChildren(); /// /// Returns a collection of all the child colliders. /// public virtual Collider[] Colliders => MeshContainer.GetComponentsInChildren(); /// /// A collection of Interactors that are currently touching the Interactable. /// public virtual IReadOnlyList TouchingInteractors => Configuration.TouchConfiguration.TouchingInteractors; /// /// A collection of Interactors that are currently grabbing the Interactable. /// public virtual IReadOnlyList GrabbingInteractors => Configuration.GrabConfiguration.GrabbingInteractors; /// /// Determines if the grab type is set to toggle. /// public virtual bool IsGrabTypeToggle => Configuration.GrabConfiguration.IsGrabTypeToggle; /// /// Whether the Interactable is currently being touched by any valid Interactor. /// public virtual bool IsTouched => TouchingInteractors.Count > 0; /// /// Whether the Interactable is currently being grabbed by any valid Interactor. /// public virtual bool IsGrabbed => GrabbingInteractors.Count > 0; /// /// Whether the touch functionality is enabled. /// public virtual bool TouchEnabled => Configuration.TouchConfiguration.TouchConsumer.gameObject.activeInHierarchy && Configuration.TouchConfiguration.UntouchConsumer.gameObject.activeInHierarchy; /// /// Whether the grab functionality is enabled. /// public virtual bool GrabEnabled => Configuration.GrabConfiguration.gameObject.activeInHierarchy; /// /// Whether the primary grab action functionality is enabled. /// public virtual bool PrimaryGrabEnabled => Configuration.GrabConfiguration.PrimaryAction.gameObject.activeInHierarchy; /// /// Whether the secondary grab action functionality is enabled. /// public virtual bool SecondaryGrabEnabled => Configuration.GrabConfiguration.PrimaryAction.gameObject.activeInHierarchy; /// /// Whether the Interactable is visible or not. Collisions will still occur on a hidden Interactable but only against a trigger collider. /// public virtual bool IsVisible { get { return Configuration.IsVisible; } set { Configuration.IsVisible = value; } } /// /// The routine for grabbing after a certain instruction. /// protected Coroutine grabRoutine; /// /// The routine for ungrabbing after a certain instruction. /// protected Coroutine ungrabRoutine; /// /// A reusable instance of . /// [Obsolete("EndOfFrame methods are now obsolete.")] protected WaitForEndOfFrame delayInstruction = new WaitForEndOfFrame(); /// /// Sets the . /// /// The index of the . public virtual void SetGrabType(int index) { GrabType = EnumExtensions.GetByIndex(index); } /// /// Attempt to grab the Interactable to the given that contains an Interactor and ungrabs any existing grabbed Interactable. /// /// The GameObject that the Interactor is on. public virtual void Grab(GameObject interactor) { Grab(interactor.TryGetComponent(true, true)); } /// /// Attempt to grab the Interactable to the given that contains an Interactor and ungrabs any existing grabbed Interactable at the end of the current frame. /// /// The GameObject that the Interactor is on. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.")] public virtual void GrabAtEndOfFrame(GameObject interactor) { Debug.LogWarning("`InteractableFacade.GrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.", gameObject); GrabAtEndOfFrame(GetInteractorFromGameObject(interactor)); } /// /// Attempt to grab the Interactable to the given that contains an Interactor and does not ungrab any existing grabbed Interactable. /// /// The GameObject that the Interactor is on. public virtual void GrabIgnoreUngrab(GameObject interactor) { GrabIgnoreUngrab(GetInteractorFromGameObject(interactor)); } /// /// Attempt to grab the Interactable to the given that contains an Interactor and does not ungrab any existing grabbed Interactable at the end of the current frame. /// /// The GameObject that the Interactor is on. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.")] public virtual void GrabIgnoreUngrabAtEndOfFrame(GameObject interactor) { Debug.LogWarning("`InteractableFacade.GrabIgnoreUngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.", gameObject); GrabIgnoreUngrabAtEndOfFrame(GetInteractorFromGameObject(interactor)); } /// /// Attempt to grab the Interactable to the given Interactor and ungrabs any existing grabbed Interactable. /// /// The Interactor to attach the Interactable to. public virtual void Grab(InteractorFacade interactor) { Configuration.GrabConfiguration.Grab(interactor); } /// /// Attempt to grab the Interactable to the given Interactor and ungrabs any existing grabbed Interactable at the end of the current frame. /// /// The Interactor to attach the Interactable to. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.")] public virtual void GrabAtEndOfFrame(InteractorFacade interactor) { Debug.LogWarning("`InteractableFacade.GrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.", gameObject); if (grabRoutine != null) { StopCoroutine(grabRoutine); } grabRoutine = StartCoroutine(DoGrabAtEndOfFrame(interactor)); } /// /// Attempt to grab the Interactable to the given Interactor and does not ungrab any existing grabbed Interactable. /// /// The Interactor to attach the Interactable to. public virtual void GrabIgnoreUngrab(InteractorFacade interactor) { Configuration.GrabConfiguration.GrabIgnoreUngrab(interactor); } /// /// Attempt to grab the Interactable to the given Interactor and does not ungrab any existing grabbed Interactable at the end of the current frame. /// /// The Interactor to attach the Interactable to. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.")] public virtual void GrabIgnoreUngrabAtEndOfFrame(InteractorFacade interactor) { Debug.LogWarning("`InteractableFacade.GrabIgnoreUngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.", gameObject); if (grabRoutine != null) { StopCoroutine(grabRoutine); } grabRoutine = StartCoroutine(DoGrabIgnoreUngrabAtEndOfFrame(interactor)); } /// /// Attempt to ungrab the Interactable to the given that contains an Interactor. /// /// The GameObject that the Interactor is on. public virtual void Ungrab(GameObject interactor) { Ungrab(GetInteractorFromGameObject(interactor)); } /// /// Attempt to ungrab the Interactable to the given that contains an Interactor at the end of the current frame. /// /// The GameObject that the Interactor is on. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")] public virtual void UngrabAtEndOfFrame(GameObject interactor) { Debug.LogWarning("`InteractableFacade.UngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.", gameObject); UngrabAtEndOfFrame(GetInteractorFromGameObject(interactor)); } /// /// Attempt to ungrab the Interactable. /// /// The Interactor to ungrab from. public virtual void Ungrab(InteractorFacade interactor) { Configuration.GrabConfiguration.Ungrab(interactor); } /// /// Attempt to ungrab the Interactable at the end of the current frame. /// /// The Interactor to ungrab from. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")] public virtual void UngrabAtEndOfFrame(InteractorFacade interactor) { Debug.LogWarning("`InteractableFacade.UngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.", gameObject); if (ungrabRoutine != null) { StopCoroutine(ungrabRoutine); } ungrabRoutine = StartCoroutine(DoUngrabAtEndOfFrame(interactor)); } /// /// Attempt to ungrab the Interactable at a specific grabbing index. /// /// The Interactor sequence index to ungrab from. public virtual void Ungrab(int sequenceIndex = 0) { Configuration.GrabConfiguration.Ungrab(sequenceIndex); } /// /// Attempt to ungrab the Interactable at a specific grabbing index at the end of the current frame. /// /// The Interactor sequence index to ungrab from. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")] public virtual void UngrabAtEndOfFrame(int sequenceIndex = 0) { Debug.LogWarning("`InteractableFacade.UngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.", gameObject); if (ungrabRoutine != null) { StopCoroutine(ungrabRoutine); } ungrabRoutine = StartCoroutine(DoUngrabAtEndOfFrame(sequenceIndex)); } /// /// Attempts to ungrab the Interactable from all Interactors. /// public virtual void UngrabAll() { Ungrab(0); } /// /// Attempts to ungrab the Interactable from all Interactors at the end of the current frame. /// [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.UngrabAll()` instead.")] public virtual void UngrabAllAtEndOfFrame() { Debug.LogWarning("`InteractableFacade.UngrabAllAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.UngrabAll()` instead.", gameObject); UngrabAtEndOfFrame(0); } /// /// Enables the touch logic. /// public virtual void EnableTouch() { Configuration.TouchConfiguration.TouchConsumer.gameObject.SetActive(true); Configuration.TouchConfiguration.UntouchConsumer.gameObject.SetActive(true); } /// /// Disables the touch logic. /// public virtual void DisableTouch() { Configuration.TouchConfiguration.UntouchAllTouchingInteractors(); Configuration.TouchConfiguration.TouchConsumer.gameObject.SetActive(false); Configuration.TouchConfiguration.UntouchConsumer.gameObject.SetActive(false); } /// /// Enables the grab logic. /// public virtual void EnableGrab() { Configuration.GrabConfiguration.gameObject.SetActive(true); } /// /// Disables the grab logic. /// public virtual void DisableGrab() { Ungrab(); Configuration.GrabConfiguration.gameObject.SetActive(false); } /// /// Enables the grab receiver logic. /// public virtual void EnableGrabReceiver() { Configuration.GrabConfiguration.GrabReceiver.gameObject.SetActive(true); } /// /// Disables the grab receiver logic. /// public virtual void DisableGrabReceiver() { Configuration.GrabConfiguration.GrabReceiver.gameObject.SetActive(false); } /// /// Enables the primary grab action logic. /// public virtual void EnablePrimaryGrabAction() { Configuration.GrabConfiguration.PrimaryAction.gameObject.SetActive(true); } /// /// Disables the primary grab action logic. /// public virtual void DisablePrimaryGrabAction() { Configuration.GrabConfiguration.Ungrab(0); Configuration.GrabConfiguration.PrimaryAction.gameObject.SetActive(false); } /// /// Enables the secondary grab action logic. /// public virtual void EnableSecondaryGrabAction() { Configuration.GrabConfiguration.SecondaryAction.gameObject.SetActive(true); } /// /// Disables the secondary grab action logic. /// public virtual void DisableSecondaryGrabAction() { Configuration.GrabConfiguration.Ungrab(1); Configuration.GrabConfiguration.SecondaryAction.gameObject.SetActive(false); } /// /// Snaps the follow on the primary and secondary action if they are type. /// public virtual void SnapFollowOrientation() { Configuration.GrabConfiguration.SnapFollowOrientation(); } protected virtual void OnEnable() { SetValidInteractionTypes(); } /// /// Gets the from the given or if not found searches for one on all descendants then ancestors. /// /// The source to search on. /// The found component if exists. protected virtual InteractorFacade GetInteractorFromGameObject(GameObject source) { return source.TryGetComponent(true, true); } /// /// Attempt to grab the Interactable to the given Interactor and ungrabs any existing grabbed Interactable at the end of the current frame. /// /// The Interactor to attach the Interactable to. /// An Enumerator to manage the running of the Coroutine. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.")] protected virtual IEnumerator DoGrabAtEndOfFrame(InteractorFacade interactor) { yield return delayInstruction; Configuration.GrabConfiguration.Grab(interactor); grabRoutine = null; } /// /// Attempt to grab the Interactable to the given Interactor and does not ungrab any existing grabbed Interactable at the end of the current frame. /// /// The Interactor to attach the Interactable to. /// An Enumerator to manage the running of the Coroutine. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.")] protected virtual IEnumerator DoGrabIgnoreUngrabAtEndOfFrame(InteractorFacade interactor) { yield return delayInstruction; Configuration.GrabConfiguration.GrabIgnoreUngrab(interactor); grabRoutine = null; } /// /// Attempt to ungrab the Interactable at the end of the current frame. /// /// The Interactor to ungrab from. /// An Enumerator to manage the running of the Coroutine. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")] protected virtual IEnumerator DoUngrabAtEndOfFrame(InteractorFacade interactor) { yield return delayInstruction; Configuration.GrabConfiguration.Ungrab(interactor); ungrabRoutine = null; } /// /// Attempt to ungrab the Interactable at a specific grabbing index at the end of the current frame. /// /// The Interactor sequence index to ungrab from. /// An Enumerator to manage the running of the Coroutine. [Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")] protected virtual IEnumerator DoUngrabAtEndOfFrame(int sequenceIndex = 0) { yield return delayInstruction; Configuration.GrabConfiguration.Ungrab(sequenceIndex); ungrabRoutine = null; } /// /// Activates or Deactivates the interaction types based on the selected . /// protected virtual void SetValidInteractionTypes() { if ((ValidInteractionTypes & InteractionTypes.Touch) == 0) { DisableTouch(); } else { EnableTouch(); } if ((ValidInteractionTypes & InteractionTypes.PrimaryGrab) == 0) { DisablePrimaryGrabAction(); } else { EnablePrimaryGrabAction(); } if ((ValidInteractionTypes & InteractionTypes.SecondaryGrab) == 0) { DisableSecondaryGrabAction(); } else { EnableSecondaryGrabAction(); } } /// /// Called after has been changed. /// protected virtual void OnAfterValidInteractionTypesChange() { SetValidInteractionTypes(); } /// /// Called after has been changed. /// protected virtual void OnAfterGrabTypeChange() { Configuration.GrabConfiguration.GrabReceiver.GrabType = GrabType; } /// /// Called after has been changed. /// protected virtual void OnAfterGrabProviderIndexChange() { Configuration.GrabConfiguration.SetGrabProvider(GrabProviderIndex); } /// /// Called after has been changed. /// protected virtual void OnAfterAttemptSwapSecondaryForPrimaryOnPrimaryDropChange() { Configuration.GrabConfiguration.ToggleAttemptSwapLogicContainer(AttemptSwapSecondaryForPrimaryOnPrimaryDrop); } } }