You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
7.9 KiB
206 lines
7.9 KiB
using Unity.Mathematics; |
|
using Unity.XR.CoreUtils.Bindings; |
|
using UnityEngine.XR.Interaction.Toolkit.AffordanceSystem.State; |
|
using UnityEngine.XR.Interaction.Toolkit.Filtering; |
|
using UnityEngine.XR.Interaction.Toolkit.Utilities.Tweenables.Primitives; |
|
|
|
namespace UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets |
|
{ |
|
/// <summary> |
|
/// Follow animation affordance for <see cref="IPokeStateDataProvider"/>, such as <see cref="XRPokeFilter"/>. |
|
/// Used to animate a pressed transform, such as a button to follow the poke position. |
|
/// </summary> |
|
[AddComponentMenu("XR/XR Poke Follow Affordance", 22)] |
|
public class XRPokeFollowAffordance : MonoBehaviour |
|
{ |
|
[SerializeField] |
|
[Tooltip("Transform that will move in the poke direction when this or a parent GameObject is poked." + |
|
"\nNote: Should be a direct child GameObject.")] |
|
Transform m_PokeFollowTransform; |
|
|
|
/// <summary> |
|
/// Transform that will animate along the axis of interaction when this interactable is poked. |
|
/// Note: Must be a direct child GameObject as it moves in local space relative to the poke target's transform. |
|
/// </summary> |
|
public Transform pokeFollowTransform |
|
{ |
|
get => m_PokeFollowTransform; |
|
set => m_PokeFollowTransform = value; |
|
} |
|
|
|
[SerializeField] |
|
[Range(0f, 20f)] |
|
[Tooltip("Multiplies transform position interpolation as a factor of Time.deltaTime. If 0, no smoothing will be applied.")] |
|
float m_SmoothingSpeed = 16f; |
|
|
|
/// <summary> |
|
/// Multiplies transform position interpolation as a factor of <see cref="Time.deltaTime"/>. If <c>0</c>, no smoothing will be applied. |
|
/// </summary> |
|
public float smoothingSpeed |
|
{ |
|
get => m_SmoothingSpeed; |
|
set => m_SmoothingSpeed = value; |
|
} |
|
|
|
[SerializeField] |
|
[Tooltip("When this component is no longer the target of the poke, the Poke Follow Transform returns to the original position.")] |
|
bool m_ReturnToInitialPosition = true; |
|
|
|
/// <summary> |
|
/// When this component is no longer the target of the poke, the <see cref="pokeFollowTransform"/> returns to the original position. |
|
/// </summary> |
|
public bool returnToInitialPosition |
|
{ |
|
get => m_ReturnToInitialPosition; |
|
set => m_ReturnToInitialPosition = value; |
|
} |
|
|
|
[SerializeField] |
|
[Tooltip("Whether to apply the follow animation if the target of the poke is a child of this transform. " + |
|
"This is useful for UI objects that may have child graphics.")] |
|
bool m_ApplyIfChildIsTarget = true; |
|
|
|
/// <summary> |
|
/// Whether to apply the follow animation if the target of the poke is a child of this transform. |
|
/// This is useful for UI objects that may have child graphics. |
|
/// </summary> |
|
public bool applyIfChildIsTarget |
|
{ |
|
get => m_ApplyIfChildIsTarget; |
|
set => m_ApplyIfChildIsTarget = value; |
|
} |
|
|
|
[SerializeField] |
|
[Tooltip("Whether to keep the Poke Follow Transform from moving past a maximum distance from the poke target.")] |
|
bool m_ClampToMaxDistance; |
|
|
|
/// <summary> |
|
/// Whether to keep the <see cref="pokeFollowTransform"/> from moving past <see cref="maxDistance"/> from the poke target. |
|
/// </summary> |
|
public bool clampToMaxDistance |
|
{ |
|
get => m_ClampToMaxDistance; |
|
set => m_ClampToMaxDistance = value; |
|
} |
|
|
|
[SerializeField] |
|
[Tooltip("The maximum distance from this transform that the Poke Follow Transform can move.")] |
|
float m_MaxDistance; |
|
|
|
/// <summary> |
|
/// The maximum distance from this transform that the <see cref="pokeFollowTransform"/> can move when |
|
/// <see cref="clampToMaxDistance"/> is <see langword="true"/>. |
|
/// </summary> |
|
public float maxDistance |
|
{ |
|
get => m_MaxDistance; |
|
set => m_MaxDistance = value; |
|
} |
|
|
|
/// <summary> |
|
/// The original position of this interactable before any pushes have been applied. |
|
/// </summary> |
|
public Vector3 initialPosition |
|
{ |
|
get => m_InitialPosition; |
|
set => m_InitialPosition = value; |
|
} |
|
|
|
IPokeStateDataProvider m_PokeDataProvider; |
|
IMultiPokeStateDataProvider m_MultiPokeStateDataProvider; |
|
|
|
readonly Vector3TweenableVariable m_TransformTweenableVariable = new Vector3TweenableVariable(); |
|
readonly BindingsGroup m_BindingsGroup = new BindingsGroup(); |
|
Vector3 m_InitialPosition; |
|
bool m_IsFirstFrame; |
|
|
|
/// <summary> |
|
/// See <see cref="MonoBehaviour"/>. |
|
/// </summary> |
|
protected void Awake() |
|
{ |
|
m_MultiPokeStateDataProvider = GetComponentInParent<IMultiPokeStateDataProvider>(); |
|
if (m_MultiPokeStateDataProvider == null) |
|
m_PokeDataProvider = GetComponentInParent<IPokeStateDataProvider>(); |
|
} |
|
|
|
/// <summary> |
|
/// See <see cref="MonoBehaviour"/>. |
|
/// </summary> |
|
protected void Start() |
|
{ |
|
if (m_PokeFollowTransform != null) |
|
{ |
|
m_InitialPosition = m_PokeFollowTransform.localPosition; |
|
m_BindingsGroup.AddBinding(m_TransformTweenableVariable.Subscribe(OnTransformTweenableVariableUpdated)); |
|
|
|
if (m_MultiPokeStateDataProvider != null) |
|
m_BindingsGroup.AddBinding(m_MultiPokeStateDataProvider.GetPokeStateDataForTarget(transform).Subscribe(OnPokeStateDataUpdated)); |
|
else if (m_PokeDataProvider != null) |
|
m_BindingsGroup.AddBinding(m_PokeDataProvider.pokeStateData.SubscribeAndUpdate(OnPokeStateDataUpdated)); |
|
} |
|
else |
|
{ |
|
enabled = false; |
|
Debug.LogWarning($"Missing Poke Follow Transform assignment on {this}. Disabling component.", this); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// See <see cref="MonoBehaviour"/>. |
|
/// </summary> |
|
protected void OnDestroy() |
|
{ |
|
m_BindingsGroup.Clear(); |
|
m_TransformTweenableVariable?.Dispose(); |
|
} |
|
|
|
/// <summary> |
|
/// See <see cref="MonoBehaviour"/>. |
|
/// </summary> |
|
protected void LateUpdate() |
|
{ |
|
if (m_IsFirstFrame) |
|
{ |
|
m_TransformTweenableVariable.HandleTween(1f); |
|
m_IsFirstFrame = false; |
|
return; |
|
} |
|
m_TransformTweenableVariable.HandleTween(m_SmoothingSpeed > 0f ? Time.deltaTime * m_SmoothingSpeed : 1f); |
|
} |
|
|
|
void OnTransformTweenableVariableUpdated(float3 position) |
|
{ |
|
m_PokeFollowTransform.localPosition = position; |
|
} |
|
|
|
void OnPokeStateDataUpdated(PokeStateData data) |
|
{ |
|
var pokeTarget = data.target; |
|
var applyFollow = m_ApplyIfChildIsTarget |
|
? pokeTarget != null && pokeTarget.IsChildOf(transform) |
|
: pokeTarget == transform; |
|
|
|
if (applyFollow) |
|
{ |
|
var targetPosition = pokeTarget.InverseTransformPoint(data.axisAlignedPokeInteractionPoint); |
|
if (m_ClampToMaxDistance && targetPosition.sqrMagnitude > m_MaxDistance * m_MaxDistance) |
|
targetPosition = Vector3.ClampMagnitude(targetPosition, m_MaxDistance); |
|
|
|
m_TransformTweenableVariable.target = targetPosition; |
|
} |
|
else if (m_ReturnToInitialPosition) |
|
{ |
|
m_TransformTweenableVariable.target = m_InitialPosition; |
|
} |
|
} |
|
|
|
public void ResetFollowTransform() |
|
{ |
|
if (!m_ClampToMaxDistance || m_PokeFollowTransform == null) |
|
return; |
|
|
|
m_PokeFollowTransform.localPosition = m_InitialPosition; |
|
} |
|
} |
|
}
|
|
|