namespace VRTK.Prefabs.Locomotion.BodyRepresentation { using UnityEngine; using UnityEngine.Events; using Malimbe.MemberChangeMethod; using Malimbe.MemberClearanceMethod; using Malimbe.XmlDocumentationAttribute; using Malimbe.PropertySerializationAttribute; using Zinnia.Data.Attribute; using VRTK.Prefabs.Interactions.Interactors; using VRTK.Prefabs.Interactions.Interactors.Collection; /// /// The public interface for the BodyRepresentation prefab. /// public class BodyRepresentationFacade : MonoBehaviour { #region Source Settings /// /// The object to follow. /// [Serialized, Cleared] [field: Header("Source Settings"), DocumentedByXml] public GameObject Source { get; set; } /// /// The thickness of to be used when resolving body collisions. /// [Serialized] [field: DocumentedByXml] public float SourceThickness { get; set; } = 0.05f; /// /// An optional offset for the to use. /// [Serialized, Cleared] [field: DocumentedByXml] public GameObject Offset { get; set; } #endregion #region Interaction Settings /// /// A collection of interactors to exclude from physics collision checks. /// [Serialized] [field: Header("Interaction Settings"), DocumentedByXml] public InteractorFacadeObservableList IgnoredInteractors { get; set; } #endregion #region Events /// /// Emitted when the body starts touching ground. /// [Header("Events"), DocumentedByXml] public UnityEvent BecameGrounded = new UnityEvent(); /// /// Emitted when the body stops touching ground. /// [DocumentedByXml] public UnityEvent BecameAirborne = new UnityEvent(); #endregion #region Reference Settings /// /// The linked Internal Setup. /// [Serialized] [field: Header("Reference Settings"), DocumentedByXml, Restricted] public BodyRepresentationProcessor Processor { get; protected set; } #endregion /// /// The object that defines the main source of truth for movement. /// public BodyRepresentationProcessor.MovementInterest Interest { get { return Processor.Interest; } set { Processor.Interest = value; } } /// /// Whether the body touches ground. /// public bool IsCharacterControllerGrounded => Processor.IsCharacterControllerGrounded; /// /// The that acts as the physical representation of the body. /// public Rigidbody PhysicsBody => Processor.PhysicsBody; /// /// Sets the source of truth for movement to come from until hits the ground, then is the new source of truth. /// /// /// This method needs to be called right before or right after applying any form of movement to the rigidbody while the body is grounded, i.e. in the same frame and before is called. /// public virtual void ListenToRigidbodyMovement() { Interest = BodyRepresentationProcessor.MovementInterest.RigidbodyUntilGrounded; } /// /// Solves body collisions by not moving the body in case it can't go to its current position. /// /// /// If body collisions should be prevented this method needs to be called right before or right after applying any form of movement to the body. /// public virtual void SolveBodyCollisions() { Processor.SolveBodyCollisions(); } protected virtual void OnEnable() { if (IgnoredInteractors == null) { return; } IgnoredInteractors.Added.AddListener(OnIgnoredInteractorAdded); IgnoredInteractors.Removed.AddListener(OnIgnoredInteractorRemoved); } protected virtual void OnDisable() { if (IgnoredInteractors == null) { return; } IgnoredInteractors.Added.RemoveListener(OnIgnoredInteractorAdded); IgnoredInteractors.Removed.RemoveListener(OnIgnoredInteractorRemoved); } /// /// Processes when a new is added to the ignored collection. /// /// The interactor to ignore collisions from. protected virtual void OnIgnoredInteractorAdded(InteractorFacade interactor) { Processor.IgnoreInteractorsCollisions(interactor); } /// /// Processes when a new is removed from the ignored collection. /// /// The interactor to resume collisions with. protected virtual void OnIgnoredInteractorRemoved(InteractorFacade interactor) { Processor.ResumeInteractorsCollisions(interactor); } /// /// Called after has been changed. /// [CalledAfterChangeOf(nameof(Source))] protected virtual void OnAfterSourceChange() { Processor.ConfigureSourceObjectFollower(); } /// /// Called after has been changed. /// [CalledAfterChangeOf(nameof(Offset))] protected virtual void OnAfterOffsetChange() { Processor.ConfigureOffsetObjectFollower(); } /// /// Called after has been changed. /// [CalledBeforeChangeOf(nameof(IgnoredInteractors))] protected virtual void OnBeforeIgnoredInteractorsChange() { if (IgnoredInteractors == null) { return; } IgnoredInteractors.Added.RemoveListener(OnIgnoredInteractorAdded); IgnoredInteractors.Removed.RemoveListener(OnIgnoredInteractorRemoved); } /// /// Called after has been changed. /// [CalledAfterChangeOf(nameof(IgnoredInteractors))] protected virtual void OnAfterIgnoredInteractorsChange() { if (IgnoredInteractors == null) { return; } IgnoredInteractors.Added.AddListener(OnIgnoredInteractorAdded); IgnoredInteractors.Removed.AddListener(OnIgnoredInteractorRemoved); } } }