#if BURST_PRESENT using Unity.Burst; #endif using System; using Unity.Mathematics; using UnityEngine.XR.Interaction.Toolkit.Utilities.Tweenables.Primitives; namespace UnityEngine.XR.Interaction.Toolkit.Utilities.Tweenables.SmartTweenableVariables { /// /// This class expands on the Quaternion tweenable variable to introduce two concepts: /// /// /// A dynamic threshold angle that grows over time in a range, /// that prevents updating the target so long as the value being assigned to the target is within that threshold. /// /// /// /// A variable speed tween () that inputs a lower and upper range speed for tweening. /// The closer the value is to the target, the faster the tween. /// /// /// /// /// #if BURST_PRESENT [BurstCompile] #endif [Obsolete("The Affordance System namespace and all associated classes have been deprecated. The existing affordance system will be moved, replaced and updated with a new interaction feedback system in a future version of XRI.")] public class SmartFollowQuaternionTweenableVariable : QuaternionTweenableVariable { /// /// Minimum angle offset allowed in degrees. /// public float minAngleAllowed { get; set; } /// /// Maximum angle offset allowed in degrees. /// public float maxAngleAllowed { get; set; } /// /// Time required to elapse before the max angle offset allowed goes from the min angle to the max. /// public float minToMaxDelaySeconds { get; set; } float m_LastUpdateTime; /// /// Constructor for SmartFollowQuaternionTweenableVariable. /// /// Minimum angle offset (in degrees) from target before which tween starts. /// Maximum angle offset (in degrees) from target before tween targets, when time threshold is reached. /// Time required to elapse (in seconds) before the max angle offset allowed goes from the min angle offset to the max. public SmartFollowQuaternionTweenableVariable( float minAngleAllowed = 0.1f, float maxAngleAllowed = 5, float minToMaxDelaySeconds = 3f) { this.minAngleAllowed = minAngleAllowed; this.maxAngleAllowed = maxAngleAllowed; this.minToMaxDelaySeconds = minToMaxDelaySeconds; } /// /// Checks if the angle difference between the current target rotation and a new target rotation is within a dynamically determined threshold, /// based on the time since the last update. /// /// The new target rotation as a Quaternion. /// Returns if the angle difference between the current and new targets is within the allowed threshold, otherwise. public bool IsNewTargetWithinThreshold(Quaternion newTarget) { float newAngleTargetOffset = Quaternion.Angle(target, newTarget); float timeSinceLastUpdate = Time.unscaledTime - m_LastUpdateTime; // Widen tolerance zone over time float allowedTargetAngleOffset = Mathf.Lerp(minAngleAllowed, maxAngleAllowed, Mathf.Clamp01(timeSinceLastUpdate / minToMaxDelaySeconds)); return newAngleTargetOffset > allowedTargetAngleOffset; } /// /// Updates the target rotation to a new value if it is within a dynamically determined threshold, /// based on the time since the last update. /// /// The new target rotation as a Quaternion. /// Returns if the target rotation is updated, otherwise. public bool SetTargetWithinThreshold(Quaternion newTarget) { bool isWithinThreshold = IsNewTargetWithinThreshold(newTarget); if (isWithinThreshold) target = newTarget; return isWithinThreshold; } /// protected override void OnTargetChanged(Quaternion newTarget) { m_LastUpdateTime = Time.unscaledTime; } /// /// Tween to new target with variable speed according to distance from target. /// The closer the target is to the current value, the faster the tween. /// /// Time elapsed since last tween. /// Lower speed limit new tween target will clamp to. /// Upper speed limit new tween target will clamp to. public void HandleSmartTween(float deltaTime, float lowerSpeed, float upperSpeed) { float angleOffsetDeg = Quaternion.Angle(target, Value); ComputeNewTweenTarget(deltaTime, angleOffsetDeg, maxAngleAllowed, lowerSpeed, upperSpeed, out float newTweenTarget); HandleTween(newTweenTarget); } #if BURST_PRESENT [BurstCompile] #endif static void ComputeNewTweenTarget(float deltaTime, float angleOffsetDeg, float maxAngleAllowed, float lowerSpeed, float upperSpeed, out float newTweenTarget) { float speedMultiplier = (1f - math.clamp(angleOffsetDeg / maxAngleAllowed, 0f, 1f)); newTweenTarget = deltaTime * math.clamp(speedMultiplier * upperSpeed, lowerSpeed, upperSpeed); } } }