// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Utilities.Solvers { /// /// Applies acceleration/velocity/friction to simulate momentum for an object being moved by other solvers/components /// [AddComponentMenu("Scripts/MRTK/SDK/Momentum")] public class Momentum : Solver { #region Momentum Parameters [SerializeField] [Tooltip("Friction to slow down the current velocity")] private float resistance = 0.99f; /// /// Friction to slow down the current velocity /// public float Resistance { get { return resistance; } set { resistance = value; } } [SerializeField] [Tooltip("Apply more resistance when going faster- applied resistance is resistance * (velocity ^ resistanceVelocityPower)")] private float resistanceVelocityPower = 1.5f; /// /// Apply more resistance when going faster- applied resistance is resistance * (velocity ^ resistanceVelocityPower) /// public float ResistanceVelocityPower { get { return resistanceVelocityPower; } set { resistanceVelocityPower = value; } } [SerializeField] [Tooltip("Accelerate to goal position at this rate")] private float accelerationRate = 10f; /// /// Accelerate to goal position at this rate /// public float AccelerationRate { get { return accelerationRate; } set { accelerationRate = value; } } [SerializeField] [Tooltip("Apply more acceleration if farther from target- applied acceleration is accelerationRate + springiness * distance")] private float springiness = 0; /// /// Apply more acceleration if farther from target- applied acceleration is accelerationRate + springiness * distance /// public float Springiness { get { return springiness; } set { springiness = value; } } [SerializeField] [Tooltip("Instantly maintain a constant depth from the view point instead of simulating Z-velocity")] private bool snapZ = true; /// /// Instantly maintain a constant depth from the view point instead of simulating Z-velocity /// public bool SnapZ { get { return snapZ; } set { snapZ = value; } } #endregion private Vector3 velocity; private Vector3 ReferencePosition => SolverHandler.TransformTarget == null ? Vector3.zero : SolverHandler.TransformTarget.position; protected override void OnEnable() { base.OnEnable(); velocity = Vector3.zero; } /// public override void SolverUpdate() { // Start with snapZ if (SnapZ) { // Snap the current depth to the goal depth var referencePosition = ReferencePosition; float goalDepth = (SolverHandler.GoalPosition - referencePosition).magnitude; Vector3 currentDelta = transform.position - referencePosition; float currentDeltaMagnitude = currentDelta.magnitude; if (!Mathf.Approximately(currentDeltaMagnitude, 0)) { Vector3 currentDeltaNorm = currentDelta / currentDeltaMagnitude; transform.position += currentDeltaNorm * (goalDepth - currentDeltaMagnitude); } } // Determine and apply acceleration Vector3 delta = SolverHandler.GoalPosition - transform.position; float deltaMagnitude = delta.magnitude; if (deltaMagnitude > 0.01f) { Vector3 deltaNorm = delta / deltaMagnitude; velocity += deltaNorm * (SolverHandler.DeltaTime * (AccelerationRate + Springiness * deltaMagnitude)); } // Resistance float velocityMagnitude = velocity.magnitude; if (!Mathf.Approximately(velocityMagnitude, 0)) { Vector3 velocityNormal = velocity / velocityMagnitude; float powFactor = velocityMagnitude > 1f ? Mathf.Pow(velocityMagnitude, ResistanceVelocityPower) : velocityMagnitude; velocity -= velocityNormal * (powFactor * Resistance * SolverHandler.DeltaTime); } if (velocity.sqrMagnitude < 0.001f) { velocity = Vector3.zero; } // Apply velocity to the solver... no wait, the actual transform transform.position += velocity * SolverHandler.DeltaTime; } /// public override void SnapTo(Vector3 position, Quaternion rotation, Vector3 scale) { base.SnapTo(position, rotation, scale); velocity = Vector3.zero; } } }