namespace VRTK.Prefabs.Interactions.Interactables.Grab { using UnityEngine; using System.Collections.Generic; using Malimbe.MemberChangeMethod; using Malimbe.MemberClearanceMethod; using Malimbe.XmlDocumentationAttribute; using Malimbe.PropertySerializationAttribute; using Zinnia.Extension; using Zinnia.Data.Attribute; using VRTK.Prefabs.Interactions.Interactors; using VRTK.Prefabs.Interactions.Interactables.Grab.Action; using VRTK.Prefabs.Interactions.Interactables.Grab.Receiver; using VRTK.Prefabs.Interactions.Interactables.Grab.Provider; /// /// Sets up the Interactable Prefab grab settings based on the provided user settings. /// public class GrabInteractableConfigurator : MonoBehaviour { #region Facade Settings /// /// The public interface facade. /// [Serialized] [field: Header("Facade Settings"), DocumentedByXml, Restricted] public InteractableFacade Facade { get; protected set; } #endregion #region Action Settings /// /// The action to perform when grabbing the interactable for the first time. /// [Serialized, Cleared] [field: Header("Action Settings"), DocumentedByXml] public GrabInteractableAction PrimaryAction { get; set; } /// /// The action to perform when grabbing the interactable for the second time. /// [Serialized, Cleared] [field: DocumentedByXml] public GrabInteractableAction SecondaryAction { get; set; } #endregion #region Reference Settings /// /// The Grab Receiver setup. /// [Serialized] [field: Header("Reference Settings"), DocumentedByXml, Restricted] public GrabInteractableReceiver GrabReceiver { get; protected set; } /// /// The Grab Provider setup. /// [Serialized] [field: Header("Reference Settings"), DocumentedByXml, Restricted] public GrabInteractableInteractorProvider GrabProvider { get; protected set; } #endregion /// /// A collection of Interactors that are currently grabbing the Interactable. /// public IReadOnlyList GrabbingInteractors => GrabProvider.GrabbingInteractors; /// /// Determines if the grab type is set to toggle. /// public bool IsGrabTypeToggle => GrabReceiver.GrabType == GrabInteractableReceiver.ActiveType.Toggle; /// /// Attempt to grab the Interactable to the given Interactor. /// /// The Interactor to attach the Interactable to. public virtual void Grab(InteractorFacade interactor) { interactor.Grab(Facade); } /// /// Attempt to ungrab the Interactable. /// /// The Interactor sequence index to ungrab from. public virtual void Ungrab(int sequenceIndex = 0) { if (GrabbingInteractors == null || GrabbingInteractors.Count == 0 || sequenceIndex >= GrabbingInteractors.Count) { return; } Ungrab(GrabbingInteractors[sequenceIndex]); } /// /// Attempts to ungrab the Interactable. /// /// The Interactor to ungrab from. public virtual void Ungrab(InteractorFacade interactor) { interactor.Ungrab(); } /// /// Notifies that the Interactable is being grabbed. /// /// The grabbing object. public virtual void NotifyGrab(GameObject data) { InteractorFacade interactor = data.TryGetComponent(true, true); if (interactor != null) { if (Facade.GrabbingInteractors.Count == 1) { Facade.FirstGrabbed?.Invoke(interactor); } Facade.Grabbed?.Invoke(interactor); interactor.NotifyOfGrab(Facade); } } /// /// Notifies that the Interactable is no longer being grabbed. /// /// The previous grabbing object. public virtual void NotifyUngrab(GameObject data) { InteractorFacade interactor = data.TryGetComponent(true, true); if (interactor != null) { Facade.Ungrabbed?.Invoke(interactor); interactor.NotifyOfUngrab(Facade); if (Facade.GrabbingInteractors.Count == 0) { Facade.LastUngrabbed?.Invoke(interactor); } } } /// /// Sets the consumer containers to the current active container. /// public virtual void ConfigureContainer() { GrabReceiver.ConfigureConsumerContainers(Facade.ConsumerContainer); ConfigureActionContainer(PrimaryAction); ConfigureActionContainer(SecondaryAction); } protected virtual void OnEnable() { LinkReceiverToProvider(); LinkToPrimaryAction(); LinkToSecondaryAction(); ConfigureContainer(); GrabReceiver.GrabValidity.ReceiveValidity = Facade.DisallowedGrabInteractors; } protected virtual void OnDisable() { UnlinkReceiverToProvider(); UnlinkToPrimaryAction(); UnlinkToSecondaryAction(); } /// /// Configures the action containers. /// /// The action to configure. protected virtual void ConfigureActionContainer(GrabInteractableAction action) { action.RunWhenActiveAndEnabled(() => action.GrabSetup = this); } /// /// Links the Grab Receiver to the Grab Provider. /// protected virtual void LinkReceiverToProvider() { GrabReceiver.OutputGrabAction.Emitted.AddListener(GrabProvider.InputGrabReceived.Receive); GrabReceiver.OutputUngrabAction.Emitted.AddListener(GrabProvider.InputUngrabReceived.Receive); GrabReceiver.OutputUngrabOnUntouchAction.Emitted.AddListener(Facade.Ungrab); } /// /// Unlinks the Grab Receiver to the Grab Provider. /// protected virtual void UnlinkReceiverToProvider() { GrabReceiver.OutputGrabAction.Emitted.RemoveListener(GrabProvider.InputGrabReceived.Receive); GrabReceiver.OutputUngrabAction.Emitted.RemoveListener(GrabProvider.InputUngrabReceived.Receive); GrabReceiver.OutputUngrabOnUntouchAction.Emitted.RemoveListener(Facade.Ungrab); } /// /// Links the Grab Receiver and Grab Provider to the Primary Grab Action. /// protected virtual void LinkToPrimaryAction() { if (PrimaryAction == null) { return; } GrabReceiver.OutputActiveCollisionConsumer.Emitted.AddListener(PrimaryAction.InputActiveCollisionConsumer.Receive); GrabProvider.OutputPrimaryGrabAction.Emitted.AddListener(PrimaryAction.InputGrabReceived.Receive); GrabProvider.OutputPrimaryUngrabAction.Emitted.AddListener(PrimaryAction.InputUngrabReceived.Receive); } /// /// Unlinks the Grab Receiver and Grab Provider to the Primary Grab Action. /// protected virtual void UnlinkToPrimaryAction() { if (PrimaryAction == null) { return; } GrabReceiver.OutputActiveCollisionConsumer.Emitted.RemoveListener(PrimaryAction.InputActiveCollisionConsumer.Receive); GrabProvider.OutputPrimaryGrabAction.Emitted.RemoveListener(PrimaryAction.InputGrabReceived.Receive); GrabProvider.OutputPrimaryUngrabAction.Emitted.RemoveListener(PrimaryAction.InputUngrabReceived.Receive); } /// /// Links the Grab Receiver and Grab Provider to the Secondary Grab Action. /// protected virtual void LinkToSecondaryAction() { if (SecondaryAction == null) { return; } GrabReceiver.OutputActiveCollisionConsumer.Emitted.AddListener(SecondaryAction.InputActiveCollisionConsumer.Receive); GrabProvider.OutputPrimaryGrabSetupOnSecondaryAction.Emitted.AddListener(SecondaryAction.InputGrabSetup.Receive); GrabProvider.OutputPrimaryUngrabResetOnSecondaryAction.Emitted.AddListener(SecondaryAction.InputUngrabReset.Receive); GrabProvider.OutputSecondaryGrabAction.Emitted.AddListener(SecondaryAction.InputGrabReceived.Receive); GrabProvider.OutputSecondaryUngrabAction.Emitted.AddListener(SecondaryAction.InputUngrabReceived.Receive); } /// /// Unlinks the Grab Receiver and Grab Provider to the Secondary Grab Action. /// protected virtual void UnlinkToSecondaryAction() { if (SecondaryAction == null) { return; } GrabReceiver.OutputActiveCollisionConsumer.Emitted.RemoveListener(SecondaryAction.InputActiveCollisionConsumer.Receive); GrabProvider.OutputPrimaryGrabSetupOnSecondaryAction.Emitted.RemoveListener(SecondaryAction.InputGrabSetup.Receive); GrabProvider.OutputPrimaryUngrabResetOnSecondaryAction.Emitted.RemoveListener(SecondaryAction.InputUngrabReset.Receive); GrabProvider.OutputSecondaryGrabAction.Emitted.RemoveListener(SecondaryAction.InputGrabReceived.Receive); GrabProvider.OutputSecondaryUngrabAction.Emitted.RemoveListener(SecondaryAction.InputUngrabReceived.Receive); } /// /// Called after has been changed. /// [CalledBeforeChangeOf(nameof(PrimaryAction))] protected virtual void OnBeforePrimaryActionChange() { UnlinkToPrimaryAction(); } /// /// Called after has been changed. /// [CalledAfterChangeOf(nameof(PrimaryAction))] protected virtual void OnAfterPrimaryActionChange() { LinkToPrimaryAction(); ConfigureActionContainer(PrimaryAction); } /// /// Called after has been changed. /// [CalledBeforeChangeOf(nameof(SecondaryAction))] protected virtual void OnBeforeSecondaryActionChange() { UnlinkToSecondaryAction(); } /// /// Called after has been changed. /// [CalledAfterChangeOf(nameof(SecondaryAction))] protected virtual void OnAfterSecondaryActionChange() { LinkToSecondaryAction(); ConfigureActionContainer(SecondaryAction); } } }