namespace VRTK.Prefabs.Interactions.Interactables.Grab.Action { using UnityEngine; using Malimbe.MemberChangeMethod; using Malimbe.XmlDocumentationAttribute; using Malimbe.PropertySerializationAttribute; using Zinnia.Extension; using Zinnia.Data.Attribute; using Zinnia.Tracking.Velocity; using Zinnia.Tracking.Follow; using Zinnia.Tracking.Follow.Modifier; /// /// Describes an action that allows the Interactable to follow an Interactor's position, rotation and scale. /// public class GrabInteractableFollowAction : GrabInteractableAction { /// /// The way in which the object is moved. /// public enum TrackingType { /// /// Updates the transform data directly, outside of the physics system. /// FollowTransform, /// /// Updates the rigidbody using velocity to stay within the bounds of the physics system. /// FollowRigidbody, /// /// Updates the rigidbody rotation by using a force at position. /// FollowRigidbodyForceRotate, /// /// Updates the transform rotation based on the position difference of the source. /// FollowTransformPositionDifferenceRotate } /// /// The offset to apply on grab. /// public enum OffsetType { /// /// No offset is applied. /// None, /// /// An offset of a specified is applied to orientate the interactable on grab. /// OrientationHandle, /// /// An offset of where the collision between the Interactor and Interactable is applied for precision grabbing. /// PrecisionPoint, /// /// The precision point offset will always be re-created based on the latest Interactor grabbing the Interactable. /// ForcePrecisionPoint } #region Interactable Settings /// /// Determines how to track the movement of interactable to the interactor. /// [Serialized] [field: Header("Interactable Settings"), DocumentedByXml] public TrackingType FollowTracking { get; set; } /// /// The offset to apply when grabbing the Interactable. /// [Serialized] [field: DocumentedByXml] public OffsetType GrabOffset { get; set; } /// /// Whether the of the interactable should be in a kinematic state when the follow action is active. /// [Serialized] [field: DocumentedByXml] public bool IsKinematicWhenActive { get; set; } = true; /// /// Whether the of the interactable should be in a kinematic state when the follow action is inactive. /// [Serialized] [field: DocumentedByXml] public bool IsKinematicWhenInactive { get; set; } /// /// Whether the property inherits the kinematic state from . /// [Serialized] [field: DocumentedByXml] public bool WillInheritIsKinematicWhenInactiveFromConsumerRigidbody { get; set; } = true; #endregion #region Tracking Settings /// /// The for tracking movement. /// [Serialized] [field: Header("Interactable Settings"), DocumentedByXml, Restricted] public ObjectFollower ObjectFollower { get; protected set; } /// /// The to move by following the transform. /// [Serialized] [field: DocumentedByXml, Restricted] public FollowModifier FollowTransformModifier { get; protected set; } /// /// The to move by applying velocities to the rigidbody. /// [Serialized] [field: DocumentedByXml, Restricted] public FollowModifier FollowRigidbodyModifier { get; protected set; } /// /// The to rotate by applying a force to the rigidbody. /// [Serialized] [field: DocumentedByXml, Restricted] public FollowModifier FollowRigidbodyForceRotateModifier { get; protected set; } /// /// The to rotate by the angle difference between the source positions. /// [Serialized] [field: DocumentedByXml, Restricted] public FollowModifier FollowTransformRotateOnPositionDifferenceModifier { get; protected set; } /// /// The to apply velocity on ungrab. /// [Serialized] [field: DocumentedByXml, Restricted] public VelocityApplier VelocityApplier { get; protected set; } #endregion #region Grab Offset Settings /// /// The container for the precision point logic. /// [Serialized] [field: Header("Grab Offset Settings"), DocumentedByXml, Restricted] public GameObject PrecisionLogicContainer { get; protected set; } /// /// The container for the precision point creation logic. /// [Serialized] [field: DocumentedByXml, Restricted] public GameObject PrecisionCreateContainer { get; protected set; } /// /// The container for the precision point force creation logic. /// [Serialized] [field: DocumentedByXml, Restricted] public GameObject PrecisionForceCreateContainer { get; protected set; } /// /// The container for the orientation handle logic. /// [Serialized] [field: DocumentedByXml, Restricted] public GameObject OrientationLogicContainer { get; protected set; } #endregion /// /// Applies the active kinematic state to the of the interactable. /// public virtual void ApplyActiveKinematicState() { GrabSetup.Facade.ConsumerRigidbody.isKinematic = IsKinematicWhenActive; } /// /// Applies the inactive kinematic state to the of the interactable. /// public virtual void ApplyInactiveKinematicState() { GrabSetup.Facade.ConsumerRigidbody.isKinematic = IsKinematicWhenInactive; } protected virtual void OnEnable() { ConfigureFollowTracking(); ConfigureGrabOffset(); } /// /// Configures the appropriate processes to be used for follow tracking based on the setting. /// protected virtual void ConfigureFollowTracking() { if (WillInheritIsKinematicWhenInactiveFromConsumerRigidbody) { IsKinematicWhenInactive = GrabSetup != null ? GrabSetup.Facade.ConsumerRigidbody.isKinematic : false; } switch (FollowTracking) { case TrackingType.FollowTransform: FollowTransformModifier.gameObject.SetActive(true); FollowRigidbodyModifier.gameObject.SetActive(false); FollowRigidbodyForceRotateModifier.gameObject.SetActive(false); FollowTransformRotateOnPositionDifferenceModifier.gameObject.SetActive(false); ObjectFollower.FollowModifier = FollowTransformModifier; IsKinematicWhenActive = true; break; case TrackingType.FollowRigidbody: FollowTransformModifier.gameObject.SetActive(false); FollowRigidbodyModifier.gameObject.SetActive(true); FollowRigidbodyForceRotateModifier.gameObject.SetActive(false); FollowTransformRotateOnPositionDifferenceModifier.gameObject.SetActive(false); ObjectFollower.FollowModifier = FollowRigidbodyModifier; IsKinematicWhenActive = false; break; case TrackingType.FollowRigidbodyForceRotate: FollowTransformModifier.gameObject.SetActive(false); FollowRigidbodyModifier.gameObject.SetActive(false); FollowRigidbodyForceRotateModifier.gameObject.SetActive(true); FollowTransformRotateOnPositionDifferenceModifier.gameObject.SetActive(false); ObjectFollower.FollowModifier = FollowRigidbodyForceRotateModifier; IsKinematicWhenActive = false; break; case TrackingType.FollowTransformPositionDifferenceRotate: FollowTransformModifier.gameObject.SetActive(false); FollowRigidbodyModifier.gameObject.SetActive(false); FollowRigidbodyForceRotateModifier.gameObject.SetActive(false); FollowTransformRotateOnPositionDifferenceModifier.gameObject.SetActive(true); ObjectFollower.FollowModifier = FollowTransformRotateOnPositionDifferenceModifier; IsKinematicWhenActive = true; break; } } /// /// Configures the appropriate processes to be used for grab offset based on the setting. /// protected virtual void ConfigureGrabOffset() { switch (GrabOffset) { case OffsetType.None: PrecisionLogicContainer.TrySetActive(false); OrientationLogicContainer.TrySetActive(false); break; case OffsetType.PrecisionPoint: PrecisionLogicContainer.TrySetActive(true); PrecisionCreateContainer.TrySetActive(true); PrecisionForceCreateContainer.TrySetActive(false); OrientationLogicContainer.TrySetActive(false); break; case OffsetType.ForcePrecisionPoint: PrecisionLogicContainer.TrySetActive(true); PrecisionForceCreateContainer.TrySetActive(true); PrecisionCreateContainer.TrySetActive(false); OrientationLogicContainer.TrySetActive(false); break; case OffsetType.OrientationHandle: PrecisionLogicContainer.TrySetActive(false); OrientationLogicContainer.TrySetActive(true); break; } } /// protected override void OnAfterGrabSetupChange() { ObjectFollower.Targets.RunWhenActiveAndEnabled(() => ObjectFollower.Targets.Clear()); ObjectFollower.Targets.RunWhenActiveAndEnabled(() => ObjectFollower.Targets.Add(GrabSetup.Facade.ConsumerContainer)); VelocityApplier.Target = GrabSetup.Facade.ConsumerRigidbody != null ? GrabSetup.Facade.ConsumerRigidbody : null; ConfigureFollowTracking(); } /// /// Called after has been changed. /// [CalledAfterChangeOf(nameof(FollowTracking))] protected virtual void OnAfterFollowTrackingChange() { ConfigureFollowTracking(); } /// /// Called after has been changed. /// [CalledAfterChangeOf(nameof(GrabOffset))] protected virtual void OnAfterGrabOffsetChange() { ConfigureGrabOffset(); } } }