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);
}
}
}
}