namespace Tilia.Interactions.Interactables.Interactables.Touch { using System.Collections.Generic; using Tilia.Interactions.Interactables.Interactors; using UnityEngine; using Zinnia.Data.Attribute; using Zinnia.Data.Collection.Counter; using Zinnia.Data.Collection.List; using Zinnia.Event.Proxy; using Zinnia.Extension; using Zinnia.Tracking.Collision; using Zinnia.Tracking.Collision.Active; using Zinnia.Tracking.Collision.Active.Operation.Extraction; public class TouchInteractableConfigurator : MonoBehaviour { #region Facade Settings [Header("Facade Settings")] [Tooltip("The public interface facade.")] [SerializeField] [Restricted] private InteractableFacade facade; /// /// The public interface facade. /// public InteractableFacade Facade { get { return facade; } set { facade = value; } } #endregion #region Touch Consumer Settings [Header("Touch Consumer Settings")] [Tooltip("The ActiveCollisionConsumer that listens for the touch payload.")] [SerializeField] [Restricted] private ActiveCollisionConsumer touchConsumer; /// /// The that listens for the touch payload. /// public ActiveCollisionConsumer TouchConsumer { get { return touchConsumer; } set { touchConsumer = value; } } [Tooltip("The ActiveCollisionConsumer that listens for the untouch payload.")] [SerializeField] [Restricted] private ActiveCollisionConsumer untouchConsumer; /// /// The that listens for the untouch payload. /// public ActiveCollisionConsumer UntouchConsumer { get { return untouchConsumer; } set { untouchConsumer = value; } } #endregion #region Touch Settings [Header("Touch Settings")] [Tooltip("The GameObjectObservableList that holds the current touching objects data.")] [SerializeField] [Restricted] private GameObjectObservableList currentTouchingObjects; /// /// The that holds the current touching objects data. /// public GameObjectObservableList CurrentTouchingObjects { get { return currentTouchingObjects; } set { currentTouchingObjects = value; } } [Tooltip("The GameObjectObservableList that holds the current untouching objects data.")] [SerializeField] [Restricted] private GameObjectObservableList currentUntouchingObjects; /// /// The that holds the current untouching objects data. /// public GameObjectObservableList CurrentUntouchingObjects { get { return currentUntouchingObjects; } set { currentUntouchingObjects = value; } } [Tooltip("The GameObjectEventProxyEmitter used to determine the untouch actions.")] [SerializeField] [Restricted] private GameObjectEventProxyEmitter currentUntouchingEventProxy; /// /// The used to determine the untouch actions. /// public GameObjectEventProxyEmitter CurrentUntouchingEventProxy { get { return currentUntouchingEventProxy; } set { currentUntouchingEventProxy = value; } } [Tooltip("The GameObjectEventProxyEmitter used to determine the touch validity.")] [SerializeField] [Restricted] private GameObjectEventProxyEmitter touchValidity; /// /// The used to determine the touch validity. /// public GameObjectEventProxyEmitter TouchValidity { get { return touchValidity; } set { touchValidity = value; } } #endregion #region Interactor Settings [Header("Interactor Settings")] [Tooltip("The ActiveCollisionsContainer for potential interactors.")] [SerializeField] [Restricted] private ActiveCollisionsContainer potentialInteractors; /// /// The for potential interactors. /// public ActiveCollisionsContainer PotentialInteractors { get { return potentialInteractors; } set { potentialInteractors = value; } } [Tooltip("The GameObjectObservableCounter for counting active interactors.")] [SerializeField] [Restricted] private GameObjectObservableCounter activeInteractorCounter; /// /// The for counting active interactors. /// public GameObjectObservableCounter ActiveInteractorCounter { get { return activeInteractorCounter; } set { activeInteractorCounter = value; } } [Tooltip("The NotifierContainerExtractor for adding active interactors.")] [SerializeField] [Restricted] private NotifierContainerExtractor addActiveInteractor; /// /// The for adding active interactors. /// public NotifierContainerExtractor AddActiveInteractor { get { return addActiveInteractor; } set { addActiveInteractor = value; } } [Tooltip("The NotifierContainerExtractor for removing active interactors.")] [SerializeField] [Restricted] private NotifierContainerExtractor removeActiveInteractor; /// /// The for removing active interactors. /// public NotifierContainerExtractor RemoveActiveInteractor { get { return removeActiveInteractor; } set { removeActiveInteractor = value; } } #endregion /// /// A collection of Interactors that are currently touching the Interactable. /// public virtual IReadOnlyList TouchingInteractors => GetTouchingInteractors(); /// /// A reusable collection to hold the returned touching interactors. /// protected readonly List touchingInteractors = new List(); /// /// Whether this is being first touched. /// protected bool isFirstTouched; /// /// Enforces that all the existing touching interactors are no longer actually touching. /// public virtual void UntouchAllTouchingInteractors() { for (int index = TouchingInteractors.Count - 1; index >= 0; index--) { CurrentUntouchingEventProxy.Receive(TouchingInteractors[index].gameObject); } } /// /// Notifies that the Interactable is being touched. /// /// The touching object. public virtual void NotifyTouch(GameObject data) { InteractorFacade interactor = data.TryGetComponent(true, true); if (interactor != null) { if (TouchingInteractors.Count <= 1 && !isFirstTouched) { isFirstTouched = true; Facade.FirstTouched?.Invoke(interactor); } Facade.Touched?.Invoke(interactor); interactor.NotifyOfTouch(Facade); } } /// /// Notifies that the Interactable is being no longer touched. /// /// The previous touching object. public virtual void NotifyUntouch(GameObject data) { InteractorFacade interactor = data.TryGetComponent(true, true); if (interactor != null) { Facade.Untouched?.Invoke(interactor); interactor.NotifyOfUntouch(Facade); if (TouchingInteractors.Count == 0) { isFirstTouched = false; Facade.LastUntouched?.Invoke(interactor); } } } /// /// Sets the consumer containers to the current active container. /// public virtual void ConfigureContainer() { TouchConsumer.Container = Facade.Configuration.ConsumerContainer; UntouchConsumer.Container = Facade.Configuration.ConsumerContainer; } /// /// Retrieves a collection of Interactors that are touching the Interactable. /// /// The touching Interactors. protected virtual IReadOnlyList GetTouchingInteractors() { touchingInteractors.Clear(); if (CurrentTouchingObjects == null) { return touchingInteractors; } foreach (GameObject element in CurrentTouchingObjects.NonSubscribableElements) { InteractorFacade interactor = element.TryGetComponent(true, true); if (interactor != null) { touchingInteractors.Add(interactor); } } return touchingInteractors; } protected virtual void OnEnable() { ConfigureContainer(); TouchValidity.ReceiveValidity = Facade.Configuration.DisallowedTouchInteractors; LinkActiveInteractorCollisions(); } protected virtual void OnDisable() { UnlinkActiveInteractorCollisions(); } /// /// Links the to the potential and active interactor logic. /// protected virtual void LinkActiveInteractorCollisions() { Facade.Configuration.CollisionNotifier.CollisionStarted.AddListener(PotentialInteractors.Add); Facade.Configuration.CollisionNotifier.CollisionStarted.AddListener(AddActiveInteractor.DoExtract); Facade.Configuration.CollisionNotifier.CollisionChanged.AddListener(ProcessPotentialInteractorContentChange); Facade.Configuration.CollisionNotifier.CollisionStopped.AddListener(PotentialInteractors.Remove); Facade.Configuration.CollisionNotifier.CollisionStopped.AddListener(RemoveActiveInteractor.DoExtract); } /// /// Unlinks the to the potential and active interactor logic. /// protected virtual void UnlinkActiveInteractorCollisions() { Facade.Configuration.CollisionNotifier.CollisionStarted.RemoveListener(PotentialInteractors.Add); Facade.Configuration.CollisionNotifier.CollisionStarted.RemoveListener(AddActiveInteractor.DoExtract); Facade.Configuration.CollisionNotifier.CollisionChanged.RemoveListener(ProcessPotentialInteractorContentChange); Facade.Configuration.CollisionNotifier.CollisionStopped.RemoveListener(PotentialInteractors.Remove); Facade.Configuration.CollisionNotifier.CollisionStopped.RemoveListener(RemoveActiveInteractor.DoExtract); } /// /// Handles any change of collision contents. /// /// The changed collision data. protected virtual void ProcessPotentialInteractorContentChange(CollisionNotifier.EventData data) { PotentialInteractors.ProcessContentsChanged(); } } }