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