namespace VRTK.Prefabs.Locomotion.Movement.SpatialManipulation { using UnityEngine; using Malimbe.XmlDocumentationAttribute; using Malimbe.PropertySerializationAttribute; using Zinnia.Extension; /// /// Manipulates the scale of the given Target based on the distance between the two sources. /// public class ScaleManipulator : SpatialManipulator { #region Scale Settings /// /// The minimum the scaled value can reach. /// [Serialized] [field: Header("Scale Settings"), DocumentedByXml] public Vector3 MinimumScale { get; set; } = Vector3.one; /// /// The maximum the scaled value can reach. /// [Serialized] [field: DocumentedByXml] public Vector3 MaximumScale { get; set; } = Vector3.one * Mathf.Infinity; /// /// Whether to scale around the offset point. /// [Serialized] [field: DocumentedByXml] public bool ScaleAroundOffset { get; set; } = true; #endregion /// /// The previous frame delta of the distance between the sources. /// protected float previousDistanceDelta; /// /// Processes the scale manipulation. /// public override void Process() { if (ActivationAction == null || !ActivationAction.Value) { wasActivated = false; return; } if (!wasActivated) { wasActivated = true; previousDistanceDelta = GetDistanceDelta(); } float currentDistanceDelata = GetDistanceDelta(); float frameDistanceDelta = currentDistanceDelata - previousDistanceDelta; if (Mathf.Abs(frameDistanceDelta) >= ActivationThreshold) { bool validOffset = ScaleAroundOffset && IsObjectValid(Offset); float initialScale = Target.transform.localScale.x; Vector3 pivotDiff = validOffset ? Target.transform.position - Offset.transform.position : Vector3.zero; Vector3 newScale = Target.transform.localScale + (Vector3.one * Time.deltaTime * Mathf.Sign(frameDistanceDelta) * Multiplier); float relativeScale = newScale.x / Target.transform.localScale.x; Vector3 finalPosition = validOffset ? Offset.transform.position + pivotDiff * relativeScale : Target.transform.position; Target.transform.localScale = new Vector3(Mathf.Clamp(newScale.x, MinimumScale.x, MaximumScale.x), Mathf.Clamp(newScale.y, MinimumScale.y, MaximumScale.y), Mathf.Clamp(newScale.z, MinimumScale.z, MaximumScale.z)); if (!Target.transform.localScale.x.ApproxEquals(initialScale)) { Target.transform.position = new Vector3(finalPosition.x, Target.transform.position.y, finalPosition.z); } } previousDistanceDelta = currentDistanceDelata; } /// /// Gets the delta of the distance between sources from the previous frame to the current frame. /// /// protected virtual float GetDistanceDelta() { bool primaryValid = IsObjectValid(PrimarySource); bool secondaryValid = IsObjectValid(SecondarySource); if (primaryValid && secondaryValid) { return Vector3.Distance(GetLocalPosition(PrimarySource), GetLocalPosition(SecondarySource)); } if (IsObjectValid(Offset) && (primaryValid || secondaryValid)) { return Vector3.Distance(primaryValid ? GetLocalPosition(PrimarySource) : GetLocalPosition(SecondarySource), Offset.transform.position); } return 0f; } } }