// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using UltimateXR.Core.Components.Composite; using UltimateXR.Manipulation; using UnityEngine; namespace UltimateXR.Examples.FullScene.Lab { /// /// Adds a spring behavior to an object whenever it is released. /// [RequireComponent(typeof(UxrGrabbableObject))] public class SpringOnRelease : UxrGrabbableObjectComponent { #region Inspector Properties/Serialized Fields [SerializeField] private float _springAmplitudeMultiplier; [SerializeField] private float _springMaxAmplitude; [SerializeField] private float _springRotAmplitudeMultiplier; [SerializeField] private float _springMaxRotAmplitude; [SerializeField] private float _springDuration; [SerializeField] private float _springFrequency; #endregion #region Unity /// /// Updates the spring if it is currently active. /// private void Update() { if (_timer > 0.0f) { _timer -= Time.deltaTime; if (_timer < 0.0f) { transform.position = _releasePosition; transform.eulerAngles = _releaseEuler; } else { float t = (_springDuration - _timer) / _springDuration; Vector3 delta = _filteredVelocity * (Mathf.Sin(-(_springDuration - _timer) * Mathf.PI * 2.0f * _springFrequency) * (1.0f - t)); transform.position = _releasePosition + delta; Vector3 deltaEuler = _filteredAngularVelocity * (Mathf.Sin(-(_springDuration - _timer) * Mathf.PI * 2.0f * _springFrequency) * (1.0f - t)); transform.eulerAngles = _releaseEuler + deltaEuler; } } } #endregion #region Event Trigger Methods /// /// Called right after the object was grabbed. /// /// Event parameters protected override void OnObjectGrabbed(UxrManipulationEventArgs e) { _timer = -1.0f; } /// /// Called right after the object was released. /// /// Event parameters protected override void OnObjectReleased(UxrManipulationEventArgs e) { if (e.IsGrabbedStateChanged) { _releasePosition = transform.position; _releaseEuler = transform.eulerAngles; _releaseVelocity = e.ReleaseVelocity; _releaseAngularVelocity = e.ReleaseAngularVelocity; _timer = _springDuration; _filteredVelocity = _releaseVelocity * _springAmplitudeMultiplier; if (_filteredVelocity.magnitude > _springMaxAmplitude) { _filteredVelocity = _filteredVelocity.normalized * _springMaxAmplitude; } _filteredAngularVelocity = _releaseAngularVelocity * _springRotAmplitudeMultiplier; if (Mathf.Abs(_filteredAngularVelocity.x) > _springMaxRotAmplitude) { _filteredAngularVelocity.x = _filteredAngularVelocity.x > 0.0f ? _springMaxRotAmplitude : -_springMaxRotAmplitude; } if (Mathf.Abs(_filteredAngularVelocity.y) > _springMaxRotAmplitude) { _filteredAngularVelocity.y = _filteredAngularVelocity.y > 0.0f ? _springMaxRotAmplitude : -_springMaxRotAmplitude; } if (Mathf.Abs(_filteredAngularVelocity.z) > _springMaxRotAmplitude) { _filteredAngularVelocity.z = _filteredAngularVelocity.z > 0.0f ? _springMaxRotAmplitude : -_springMaxRotAmplitude; } } } #endregion #region Private Types & Data private Vector3 _releasePosition; private Vector3 _releaseEuler; private Vector3 _releaseVelocity; private Vector3 _releaseAngularVelocity; private Vector3 _filteredVelocity; private Vector3 _filteredAngularVelocity; private float _timer; #endregion } }