namespace Tilia.Interactions.Interactables.Interactables
{
using System;
using System.Collections;
using System.Collections.Generic;
using Tilia.Interactions.Interactables.Interactables.Grab.Provider;
using Tilia.Interactions.Interactables.Interactables.Grab.Receiver;
using Tilia.Interactions.Interactables.Interactors;
using UnityEngine;
using UnityEngine.Events;
using Zinnia.Data.Attribute;
using Zinnia.Extension;
///
/// The public interface into the Interactable Prefab.
///
public class InteractableFacade : MonoBehaviour
{
///
/// Defines the event with the .
///
[Serializable]
public class UnityEvent : UnityEvent { }
///
/// The allowed interaction states.
///
[Flags]
public enum InteractionTypes
{
///
/// Whether the Interactable can be touched.
///
Touch = 1 << 0,
///
/// Whether the Interactable can be primarily grabbed.
///
PrimaryGrab = 1 << 1,
///
/// Whether the Interactable can be secondarily grabbed.
///
SecondaryGrab = 1 << 2,
}
#region Reference Settings
[Header("Reference Settings")]
[Tooltip("The linked InteractableConfigurator.")]
[SerializeField]
[Restricted]
private InteractableConfigurator configuration;
///
/// The linked .
///
public InteractableConfigurator Configuration
{
get
{
return configuration;
}
set
{
configuration = value;
}
}
[Tooltip("The types of interaction that are valid on the Interactable.")]
[SerializeField]
[UnityFlags]
private InteractionTypes validInteractionTypes = (InteractionTypes)(-1);
///
/// The types of interaction that are valid on the Interactable.
///
public InteractionTypes ValidInteractionTypes
{
get
{
return validInteractionTypes;
}
set
{
validInteractionTypes = value;
if (this.IsMemberChangeAllowed())
{
OnAfterValidInteractionTypesChange();
}
}
}
#endregion
#region Touch Events
///
/// Emitted when the Interactable is touched for the first time by an Interactor.
///
[Header("Touch Events")]
public UnityEvent FirstTouched = new UnityEvent();
///
/// Emitted when an Interactor touches the Interactable.
///
public UnityEvent Touched = new UnityEvent();
///
/// Emitted when an Interactor stops touching the Interactable.
///
public UnityEvent Untouched = new UnityEvent();
///
/// Emitted when the Interactable is untouched for the last time by an Interactor.
///
public UnityEvent LastUntouched = new UnityEvent();
#endregion
#region Grab Events
///
/// Emitted when the Interactable is grabbed for the first time by an Interactor.
///
[Header("Grab Events")]
public UnityEvent FirstGrabbed = new UnityEvent();
///
/// Emitted when an Interactor grabs the Interactable.
///
public UnityEvent Grabbed = new UnityEvent();
///
/// Emitted when an Interactor ungrabs the Interactable.
///
public UnityEvent Ungrabbed = new UnityEvent();
///
/// Emitted when the Interactable is ungrabbed for the last time by an Interactor.
///
public UnityEvent LastUngrabbed = new UnityEvent();
#endregion
#region Grab Action Settings
[Header("Grab Action Settings")]
[Tooltip("The linked GrabInteractableReceiver.ActiveType.")]
[SerializeField]
private GrabInteractableReceiver.ActiveType grabType;
///
/// The linked .
///
public GrabInteractableReceiver.ActiveType GrabType
{
get
{
return grabType;
}
set
{
grabType = value;
if (this.IsMemberChangeAllowed())
{
OnAfterGrabTypeChange();
}
}
}
[Tooltip("The GrabInteractableInteractorProvider to use.")]
[SerializeField]
private int grabProviderIndex;
///
/// The to use.
///
public int GrabProviderIndex
{
get
{
return grabProviderIndex;
}
set
{
grabProviderIndex = value;
if (this.IsMemberChangeAllowed())
{
OnAfterGrabProviderIndexChange();
}
}
}
[Tooltip("Attempts to swap the Secondary action to become the primary action if the primary interactor ungrabs causing a force ungrab of the secondary interactor.")]
[SerializeField]
private bool attemptSwapSecondaryForPrimaryOnPrimaryDrop;
///
/// Attempts to swap the Secondary action to become the primary action if the primary interactor ungrabs causing a force ungrab of the secondary interactor.
///
public bool AttemptSwapSecondaryForPrimaryOnPrimaryDrop
{
get
{
return attemptSwapSecondaryForPrimaryOnPrimaryDrop;
}
set
{
attemptSwapSecondaryForPrimaryOnPrimaryDrop = value;
if (this.IsMemberChangeAllowed())
{
OnAfterAttemptSwapSecondaryForPrimaryOnPrimaryDropChange();
}
}
}
#endregion
///
/// The attached to the Interactable.
///
public virtual Rigidbody InteractableRigidbody => Configuration.ConsumerRigidbody;
///
/// The mesh container.
///
public virtual GameObject MeshContainer => Configuration.MeshContainer;
///
/// Returns a collection of all the child meshes.
///
public virtual MeshRenderer[] Meshes => MeshContainer.GetComponentsInChildren();
///
/// Returns a collection of all the child colliders.
///
public virtual Collider[] Colliders => MeshContainer.GetComponentsInChildren();
///
/// A collection of Interactors that are currently touching the Interactable.
///
public virtual IReadOnlyList TouchingInteractors => Configuration.TouchConfiguration.TouchingInteractors;
///
/// A collection of Interactors that are currently grabbing the Interactable.
///
public virtual IReadOnlyList GrabbingInteractors => Configuration.GrabConfiguration.GrabbingInteractors;
///
/// Determines if the grab type is set to toggle.
///
public virtual bool IsGrabTypeToggle => Configuration.GrabConfiguration.IsGrabTypeToggle;
///
/// Whether the Interactable is currently being touched by any valid Interactor.
///
public virtual bool IsTouched => TouchingInteractors.Count > 0;
///
/// Whether the Interactable is currently being grabbed by any valid Interactor.
///
public virtual bool IsGrabbed => GrabbingInteractors.Count > 0;
///
/// Whether the touch functionality is enabled.
///
public virtual bool TouchEnabled => Configuration.TouchConfiguration.TouchConsumer.gameObject.activeInHierarchy && Configuration.TouchConfiguration.UntouchConsumer.gameObject.activeInHierarchy;
///
/// Whether the grab functionality is enabled.
///
public virtual bool GrabEnabled => Configuration.GrabConfiguration.gameObject.activeInHierarchy;
///
/// Whether the primary grab action functionality is enabled.
///
public virtual bool PrimaryGrabEnabled => Configuration.GrabConfiguration.PrimaryAction.gameObject.activeInHierarchy;
///
/// Whether the secondary grab action functionality is enabled.
///
public virtual bool SecondaryGrabEnabled => Configuration.GrabConfiguration.PrimaryAction.gameObject.activeInHierarchy;
///
/// Whether the Interactable is visible or not. Collisions will still occur on a hidden Interactable but only against a trigger collider.
///
public virtual bool IsVisible
{
get
{
return Configuration.IsVisible;
}
set
{
Configuration.IsVisible = value;
}
}
///
/// The routine for grabbing after a certain instruction.
///
protected Coroutine grabRoutine;
///
/// The routine for ungrabbing after a certain instruction.
///
protected Coroutine ungrabRoutine;
///
/// A reusable instance of .
///
[Obsolete("EndOfFrame methods are now obsolete.")]
protected WaitForEndOfFrame delayInstruction = new WaitForEndOfFrame();
///
/// Sets the .
///
/// The index of the .
public virtual void SetGrabType(int index)
{
GrabType = EnumExtensions.GetByIndex(index);
}
///
/// Attempt to grab the Interactable to the given that contains an Interactor and ungrabs any existing grabbed Interactable.
///
/// The GameObject that the Interactor is on.
public virtual void Grab(GameObject interactor)
{
Grab(interactor.TryGetComponent(true, true));
}
///
/// Attempt to grab the Interactable to the given that contains an Interactor and ungrabs any existing grabbed Interactable at the end of the current frame.
///
/// The GameObject that the Interactor is on.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.")]
public virtual void GrabAtEndOfFrame(GameObject interactor)
{
Debug.LogWarning("`InteractableFacade.GrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.", gameObject);
GrabAtEndOfFrame(GetInteractorFromGameObject(interactor));
}
///
/// Attempt to grab the Interactable to the given that contains an Interactor and does not ungrab any existing grabbed Interactable.
///
/// The GameObject that the Interactor is on.
public virtual void GrabIgnoreUngrab(GameObject interactor)
{
GrabIgnoreUngrab(GetInteractorFromGameObject(interactor));
}
///
/// Attempt to grab the Interactable to the given that contains an Interactor and does not ungrab any existing grabbed Interactable at the end of the current frame.
///
/// The GameObject that the Interactor is on.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.")]
public virtual void GrabIgnoreUngrabAtEndOfFrame(GameObject interactor)
{
Debug.LogWarning("`InteractableFacade.GrabIgnoreUngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.", gameObject);
GrabIgnoreUngrabAtEndOfFrame(GetInteractorFromGameObject(interactor));
}
///
/// Attempt to grab the Interactable to the given Interactor and ungrabs any existing grabbed Interactable.
///
/// The Interactor to attach the Interactable to.
public virtual void Grab(InteractorFacade interactor)
{
Configuration.GrabConfiguration.Grab(interactor);
}
///
/// Attempt to grab the Interactable to the given Interactor and ungrabs any existing grabbed Interactable at the end of the current frame.
///
/// The Interactor to attach the Interactable to.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.")]
public virtual void GrabAtEndOfFrame(InteractorFacade interactor)
{
Debug.LogWarning("`InteractableFacade.GrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.", gameObject);
if (grabRoutine != null)
{
StopCoroutine(grabRoutine);
}
grabRoutine = StartCoroutine(DoGrabAtEndOfFrame(interactor));
}
///
/// Attempt to grab the Interactable to the given Interactor and does not ungrab any existing grabbed Interactable.
///
/// The Interactor to attach the Interactable to.
public virtual void GrabIgnoreUngrab(InteractorFacade interactor)
{
Configuration.GrabConfiguration.GrabIgnoreUngrab(interactor);
}
///
/// Attempt to grab the Interactable to the given Interactor and does not ungrab any existing grabbed Interactable at the end of the current frame.
///
/// The Interactor to attach the Interactable to.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.")]
public virtual void GrabIgnoreUngrabAtEndOfFrame(InteractorFacade interactor)
{
Debug.LogWarning("`InteractableFacade.GrabIgnoreUngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.", gameObject);
if (grabRoutine != null)
{
StopCoroutine(grabRoutine);
}
grabRoutine = StartCoroutine(DoGrabIgnoreUngrabAtEndOfFrame(interactor));
}
///
/// Attempt to ungrab the Interactable to the given that contains an Interactor.
///
/// The GameObject that the Interactor is on.
public virtual void Ungrab(GameObject interactor)
{
Ungrab(GetInteractorFromGameObject(interactor));
}
///
/// Attempt to ungrab the Interactable to the given that contains an Interactor at the end of the current frame.
///
/// The GameObject that the Interactor is on.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")]
public virtual void UngrabAtEndOfFrame(GameObject interactor)
{
Debug.LogWarning("`InteractableFacade.UngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.", gameObject);
UngrabAtEndOfFrame(GetInteractorFromGameObject(interactor));
}
///
/// Attempt to ungrab the Interactable.
///
/// The Interactor to ungrab from.
public virtual void Ungrab(InteractorFacade interactor)
{
Configuration.GrabConfiguration.Ungrab(interactor);
}
///
/// Attempt to ungrab the Interactable at the end of the current frame.
///
/// The Interactor to ungrab from.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")]
public virtual void UngrabAtEndOfFrame(InteractorFacade interactor)
{
Debug.LogWarning("`InteractableFacade.UngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.", gameObject);
if (ungrabRoutine != null)
{
StopCoroutine(ungrabRoutine);
}
ungrabRoutine = StartCoroutine(DoUngrabAtEndOfFrame(interactor));
}
///
/// Attempt to ungrab the Interactable at a specific grabbing index.
///
/// The Interactor sequence index to ungrab from.
public virtual void Ungrab(int sequenceIndex = 0)
{
Configuration.GrabConfiguration.Ungrab(sequenceIndex);
}
///
/// Attempt to ungrab the Interactable at a specific grabbing index at the end of the current frame.
///
/// The Interactor sequence index to ungrab from.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")]
public virtual void UngrabAtEndOfFrame(int sequenceIndex = 0)
{
Debug.LogWarning("`InteractableFacade.UngrabAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.", gameObject);
if (ungrabRoutine != null)
{
StopCoroutine(ungrabRoutine);
}
ungrabRoutine = StartCoroutine(DoUngrabAtEndOfFrame(sequenceIndex));
}
///
/// Attempts to ungrab the Interactable from all Interactors.
///
public virtual void UngrabAll()
{
Ungrab(0);
}
///
/// Attempts to ungrab the Interactable from all Interactors at the end of the current frame.
///
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.UngrabAll()` instead.")]
public virtual void UngrabAllAtEndOfFrame()
{
Debug.LogWarning("`InteractableFacade.UngrabAllAtEndOfFrame()` has been deprecated. Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.UngrabAll()` instead.", gameObject);
UngrabAtEndOfFrame(0);
}
///
/// Enables the touch logic.
///
public virtual void EnableTouch()
{
Configuration.TouchConfiguration.TouchConsumer.gameObject.SetActive(true);
Configuration.TouchConfiguration.UntouchConsumer.gameObject.SetActive(true);
}
///
/// Disables the touch logic.
///
public virtual void DisableTouch()
{
Configuration.TouchConfiguration.UntouchAllTouchingInteractors();
Configuration.TouchConfiguration.TouchConsumer.gameObject.SetActive(false);
Configuration.TouchConfiguration.UntouchConsumer.gameObject.SetActive(false);
}
///
/// Enables the grab logic.
///
public virtual void EnableGrab()
{
Configuration.GrabConfiguration.gameObject.SetActive(true);
}
///
/// Disables the grab logic.
///
public virtual void DisableGrab()
{
Ungrab();
Configuration.GrabConfiguration.gameObject.SetActive(false);
}
///
/// Enables the grab receiver logic.
///
public virtual void EnableGrabReceiver()
{
Configuration.GrabConfiguration.GrabReceiver.gameObject.SetActive(true);
}
///
/// Disables the grab receiver logic.
///
public virtual void DisableGrabReceiver()
{
Configuration.GrabConfiguration.GrabReceiver.gameObject.SetActive(false);
}
///
/// Enables the primary grab action logic.
///
public virtual void EnablePrimaryGrabAction()
{
Configuration.GrabConfiguration.PrimaryAction.gameObject.SetActive(true);
}
///
/// Disables the primary grab action logic.
///
public virtual void DisablePrimaryGrabAction()
{
Configuration.GrabConfiguration.Ungrab(0);
Configuration.GrabConfiguration.PrimaryAction.gameObject.SetActive(false);
}
///
/// Enables the secondary grab action logic.
///
public virtual void EnableSecondaryGrabAction()
{
Configuration.GrabConfiguration.SecondaryAction.gameObject.SetActive(true);
}
///
/// Disables the secondary grab action logic.
///
public virtual void DisableSecondaryGrabAction()
{
Configuration.GrabConfiguration.Ungrab(1);
Configuration.GrabConfiguration.SecondaryAction.gameObject.SetActive(false);
}
///
/// Snaps the follow on the primary and secondary action if they are type.
///
public virtual void SnapFollowOrientation()
{
Configuration.GrabConfiguration.SnapFollowOrientation();
}
protected virtual void OnEnable()
{
SetValidInteractionTypes();
}
///
/// Gets the from the given or if not found searches for one on all descendants then ancestors.
///
/// The source to search on.
/// The found component if exists.
protected virtual InteractorFacade GetInteractorFromGameObject(GameObject source)
{
return source.TryGetComponent(true, true);
}
///
/// Attempt to grab the Interactable to the given Interactor and ungrabs any existing grabbed Interactable at the end of the current frame.
///
/// The Interactor to attach the Interactable to.
/// An Enumerator to manage the running of the Coroutine.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Grab()` instead.")]
protected virtual IEnumerator DoGrabAtEndOfFrame(InteractorFacade interactor)
{
yield return delayInstruction;
Configuration.GrabConfiguration.Grab(interactor);
grabRoutine = null;
}
///
/// Attempt to grab the Interactable to the given Interactor and does not ungrab any existing grabbed Interactable at the end of the current frame.
///
/// The Interactor to attach the Interactable to.
/// An Enumerator to manage the running of the Coroutine.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.GrabIgnoreUngrab()` instead.")]
protected virtual IEnumerator DoGrabIgnoreUngrabAtEndOfFrame(InteractorFacade interactor)
{
yield return delayInstruction;
Configuration.GrabConfiguration.GrabIgnoreUngrab(interactor);
grabRoutine = null;
}
///
/// Attempt to ungrab the Interactable at the end of the current frame.
///
/// The Interactor to ungrab from.
/// An Enumerator to manage the running of the Coroutine.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")]
protected virtual IEnumerator DoUngrabAtEndOfFrame(InteractorFacade interactor)
{
yield return delayInstruction;
Configuration.GrabConfiguration.Ungrab(interactor);
ungrabRoutine = null;
}
///
/// Attempt to ungrab the Interactable at a specific grabbing index at the end of the current frame.
///
/// The Interactor sequence index to ungrab from.
/// An Enumerator to manage the running of the Coroutine.
[Obsolete("Use `WaitForEndOfFrameYieldEmitter.Yielded() -> InteractableFacade.Ungrab()` instead.")]
protected virtual IEnumerator DoUngrabAtEndOfFrame(int sequenceIndex = 0)
{
yield return delayInstruction;
Configuration.GrabConfiguration.Ungrab(sequenceIndex);
ungrabRoutine = null;
}
///
/// Activates or Deactivates the interaction types based on the selected .
///
protected virtual void SetValidInteractionTypes()
{
if ((ValidInteractionTypes & InteractionTypes.Touch) == 0)
{
DisableTouch();
}
else
{
EnableTouch();
}
if ((ValidInteractionTypes & InteractionTypes.PrimaryGrab) == 0)
{
DisablePrimaryGrabAction();
}
else
{
EnablePrimaryGrabAction();
}
if ((ValidInteractionTypes & InteractionTypes.SecondaryGrab) == 0)
{
DisableSecondaryGrabAction();
}
else
{
EnableSecondaryGrabAction();
}
}
///
/// Called after has been changed.
///
protected virtual void OnAfterValidInteractionTypesChange()
{
SetValidInteractionTypes();
}
///
/// Called after has been changed.
///
protected virtual void OnAfterGrabTypeChange()
{
Configuration.GrabConfiguration.GrabReceiver.GrabType = GrabType;
}
///
/// Called after has been changed.
///
protected virtual void OnAfterGrabProviderIndexChange()
{
Configuration.GrabConfiguration.SetGrabProvider(GrabProviderIndex);
}
///
/// Called after has been changed.
///
protected virtual void OnAfterAttemptSwapSecondaryForPrimaryOnPrimaryDropChange()
{
Configuration.GrabConfiguration.ToggleAttemptSwapLogicContainer(AttemptSwapSecondaryForPrimaryOnPrimaryDrop);
}
}
}