namespace VRTK.Prefabs.Interactions.InteractableSnapZone { using UnityEngine; using Malimbe.XmlDocumentationAttribute; using Malimbe.PropertySerializationAttribute; using Zinnia.Extension; using Zinnia.Event.Proxy; using Zinnia.Data.Type; using Zinnia.Data.Attribute; using Zinnia.Data.Collection.List; using Zinnia.Rule.Collection; using Zinnia.Tracking.Modification; using VRTK.Prefabs.Interactions.Interactables; using VRTK.Prefabs.Interactions.Interactables.Grab; public class SnapZoneConfigurator : MonoBehaviour { #region Facade Settings /// /// The public interface facade. /// [Serialized] [field: Header("Facade Settings"), DocumentedByXml, Restricted] public SnapZoneFacade Facade { get; protected set; } #endregion #region Reference Settings /// /// The that determines the valid snappable Interactables. /// [Serialized] [field: Header("Reference Settings"), DocumentedByXml] public RuleContainerObservableList ValidCollisionRules { get; protected set; } /// /// The that processes if the interactable entering the zone is being grabbed. /// [Serialized] [field: DocumentedByXml, Restricted] public InteractableGrabStateEmitter GrabStateEmitter { get; protected set; } /// /// The that determines if the activation of the zone is valid. /// [Serialized] [field: DocumentedByXml, Restricted] public SnapZoneActivator ActivationArea { get; protected set; } /// /// The that determines if the activation of the zone is valid. /// [Serialized] [field: DocumentedByXml, Restricted] public ActivationValidator ActivationValidator { get; protected set; } /// /// The that transitions the Interactable to the snapped destination. /// [Serialized] [field: DocumentedByXml, Restricted] public TransformPropertyApplier PropertyApplier { get; protected set; } /// /// The containing the list of objects that are currently colliding with the zone. /// [Serialized] [field: DocumentedByXml, Restricted] public GameObjectObservableList CollidingObjectsList { get; protected set; } /// /// The containing the list of Interactables that can be snapped. /// [Serialized] [field: DocumentedByXml, Restricted] public GameObjectObservableList ValidSnappableInteractablesList { get; protected set; } /// /// The containing the list of snapped Interactables. /// [Serialized] [field: DocumentedByXml, Restricted] public GameObjectObservableList SnappedInteractablesList { get; protected set; } /// /// The that is responsible for processing the snap of a valid dropped Interactable. /// [Serialized] [field: DocumentedByXml, Restricted] public GameObjectEventProxyEmitter SnapDroppedInteractableProcess { get; protected set; } /// /// The that is responsible for forcing an unsnap of the snapped Interactable. /// [Serialized] [field: DocumentedByXml, Restricted] public GameObjectEventProxyEmitter ForceUnsnapInteractableProcess { get; protected set; } #endregion /// /// Returns the collection of s that are currently colliding with the snap zone and are valid to be snapped. /// public HeapAllocationFreeReadOnlyList SnappableInteractables => ValidSnappableInteractablesList.NonSubscribableElements; /// /// Returns the currently snapped . /// public GameObject SnappedInteractable => SnappedInteractablesList.NonSubscribableElements.Count > 0 ? SnappedInteractablesList.NonSubscribableElements[0] : null; /// /// Attempts to snap a given to the snap zone. /// /// The object to attempt to snap. public virtual void Snap(GameObject objectToSnap) { if (SnappedInteractable != null) { return; } SnapDroppedInteractableProcess.Receive(objectToSnap); } /// /// Attempts to unsnap any existing that is currently snapped to the snap zone. /// public virtual void Unsnap() { if (SnappedInteractable == null) { return; } ForceUnsnapInteractableProcess.Receive(SnappedInteractable.gameObject); } /// /// Emits the Entered event. /// /// The that has entered the zone. public virtual void EmitEntered(GameObject entered) { if (entered == null) { return; } Facade.Entered?.Invoke(entered); } /// /// Emits the Exited event. /// /// The that has exited the zone. public virtual void EmitExited(GameObject exited) { if (exited == null) { return; } Facade.Exited?.Invoke(exited); } /// /// Emits the Activated event. /// /// The that has activated the zone. public virtual void EmitActivated(GameObject activator) { if (activator == null) { return; } Facade.Activated?.Invoke(activator); } /// /// Emits the Deactivated event. /// /// The that has deactivated the zone. public virtual void EmitDeactivated(GameObject deactivator) { if (deactivator == null) { return; } Facade.Deactivated?.Invoke(deactivator); } /// /// Emits the Snapped event. /// /// The is snapped to the zone. public virtual void EmitSnapped(GameObject snapped) { if (snapped == null) { return; } Facade.Snapped?.Invoke(snapped); } /// /// Emits the Unsnapped event. /// /// The is unsnapped from the zone. public virtual void EmitUnsnapped(GameObject unsnapped) { if (unsnapped == null) { return; } Facade.Unsnapped?.Invoke(unsnapped); } /// /// Configures the validity rules for the snap zone. /// public virtual void ConfigureValidityRules() { if (ValidCollisionRules.NonSubscribableElements.Count > 1) { ValidCollisionRules.RunWhenActiveAndEnabled(() => ValidCollisionRules.RemoveAt(1)); } if (Facade.SnapValidity.Interface != null) { ValidCollisionRules.RunWhenActiveAndEnabled(() => ValidCollisionRules.Add(Facade.SnapValidity)); } } /// /// Configures the transition duration for the snapping process. /// public virtual void ConfigurePropertyApplier() { PropertyApplier.RunWhenActiveAndEnabled(() => PropertyApplier.TransitionDuration = Facade.TransitionDuration); } /// /// Attempts to process any other valid snappable objects against any other potential SnapZones if their primary activating zone is snapped by another object. /// public virtual void ProcessOtherSnappablesOnSnap() { foreach (GameObject snappable in SnappableInteractables) { if (snappable == null || snappable == SnappedInteractable) { continue; } InteractableFacade snappableInteractable = snappable.TryGetComponent(true, true); if (snappableInteractable == null) { continue; } foreach (GameObject collidingWith in snappableInteractable.ActiveCollisions.SubscribableElements) { SnapZoneActivator activatingZone = collidingWith.GetComponent(); if (activatingZone == null || activatingZone == ActivationArea || activatingZone.Facade.ZoneState == SnapZoneFacade.SnapZoneState.ZoneIsActivated) { continue; } activatingZone.Facade.Configuration.GrabStateEmitter.DoIsGrabbed(snappableInteractable); } } } protected virtual void OnEnable() { ConfigureValidityRules(); ConfigurePropertyApplier(); if (SnappedInteractable != null) { SnappedInteractable.gameObject.SetActive(true); } } protected virtual void OnDisable() { if (SnappedInteractable != null) { SnappedInteractable.gameObject.SetActive(false); } } } }