namespace VRTK.Prefabs.Interactions.Interactables.Touch { using UnityEngine; using System.Collections.Generic; using Malimbe.XmlDocumentationAttribute; using Malimbe.PropertySerializationAttribute; using Zinnia.Extension; using Zinnia.Event.Proxy; using Zinnia.Data.Attribute; using Zinnia.Data.Collection.List; using Zinnia.Tracking.Collision; using Zinnia.Tracking.Collision.Active; using Zinnia.Tracking.Collision.Active.Operation.Extraction; using VRTK.Prefabs.Interactions.Interactors; public class TouchInteractableConfigurator : MonoBehaviour { #region Facade Settings /// /// The public interface facade. /// [Serialized] [field: Header("Facade Settings"), DocumentedByXml, Restricted] public InteractableFacade Facade { get; protected set; } #endregion #region Touch Consumer Settings /// /// The that listens for the touch payload. /// [Serialized] [field: Header("Touch Consumer Settings"), DocumentedByXml, Restricted] public ActiveCollisionConsumer TouchConsumer { get; protected set; } /// /// The that listens for the untouch payload. /// [Serialized] [field: DocumentedByXml, Restricted] public ActiveCollisionConsumer UntouchConsumer { get; protected set; } #endregion #region Touch Settings /// /// The that holds the current touching objects data. /// [Serialized] [field: Header("Touch Settings"), DocumentedByXml, Restricted] public GameObjectObservableList CurrentTouchingObjects { get; protected set; } /// /// The used to determine the touch validity. /// [Serialized] [field: DocumentedByXml, Restricted] public GameObjectEventProxyEmitter TouchValidity { get; set; } #endregion #region Interactor Settings /// /// The for potential interactors. /// [Serialized] [field: Header("Interactor Settings"), DocumentedByXml, Restricted] public ActiveCollisionsContainer PotentialInteractors { get; protected set; } /// /// The for adding active interactors. /// [Serialized] [field: DocumentedByXml, Restricted] public NotifierContainerExtractor AddActiveInteractor { get; protected set; } /// /// The for removing active interactors. /// [Serialized] [field: DocumentedByXml, Restricted] public NotifierContainerExtractor RemoveActiveInteractor { get; protected set; } #endregion /// /// A collection of Interactors that are currently touching the Interactable. /// public IReadOnlyList TouchingInteractors => GetTouchingInteractors(); /// /// A reusable collection to hold the returned touching interactors. /// protected readonly List touchingInteractors = new List(); /// /// 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 (Facade.TouchingInteractors.Count == 1) { 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 (Facade.TouchingInteractors.Count == 0) { Facade.LastUntouched?.Invoke(interactor); } } } /// /// Sets the consumer containers to the current active container. /// public virtual void ConfigureContainer() { TouchConsumer.Container = Facade.ConsumerContainer; UntouchConsumer.Container = Facade.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.DisallowedTouchInteractors; LinkActiveInteractorCollisions(); } protected virtual void OnDisable() { UnlinkActiveInteractorCollisions(); } /// /// Links the to the potential and active interactor logic. /// protected virtual void LinkActiveInteractorCollisions() { Facade.CollisionNotifier.CollisionStarted.AddListener(PotentialInteractors.Add); Facade.CollisionNotifier.CollisionStarted.AddListener(AddActiveInteractor.DoExtract); Facade.CollisionNotifier.CollisionChanged.AddListener(ProcessPotentialInteractorContentChange); Facade.CollisionNotifier.CollisionStopped.AddListener(PotentialInteractors.Remove); Facade.CollisionNotifier.CollisionStopped.AddListener(RemoveActiveInteractor.DoExtract); } /// /// Unlinks the to the potential and active interactor logic. /// protected virtual void UnlinkActiveInteractorCollisions() { Facade.CollisionNotifier.CollisionStarted.RemoveListener(PotentialInteractors.Add); Facade.CollisionNotifier.CollisionStarted.RemoveListener(AddActiveInteractor.DoExtract); Facade.CollisionNotifier.CollisionChanged.RemoveListener(ProcessPotentialInteractorContentChange); Facade.CollisionNotifier.CollisionStopped.RemoveListener(PotentialInteractors.Remove); Facade.CollisionNotifier.CollisionStopped.RemoveListener(RemoveActiveInteractor.DoExtract); } /// /// Handles any change of collision contents. /// /// The changed collision data. protected virtual void ProcessPotentialInteractorContentChange(CollisionNotifier.EventData data) { PotentialInteractors.ProcessContentsChanged(); } } }