using System; using UnityEngine; using UnityEngine.Events; namespace OmiLAXR { /// /// Monitors a GameObject's transform for significant changes in position, rotation, and scale. /// Triggers events when changes exceed defined thresholds. /// [AddComponentMenu("OmiLAXR / Game Objects / Transform Watcher")] [DisallowMultipleComponent] public class TransformWatcher : MonoBehaviour { /// /// Data structure to hold information about a transform property change, /// including both the previous and current values. /// public struct TransformChange { /// /// The previous value before the change occurred. /// public Vector3 OldValue; /// /// The new value after the change occurred. /// public Vector3 NewValue; } [Serializable] public struct TransformIgnore { public bool position; public bool rotation; public bool scale; } public TransformIgnore ignore; /// /// Minimum position change (in units) required to trigger the position change event. /// [Tooltip("Minimum position change (in units) required to trigger events")] public float positionThreshold = .5f; /// /// Minimum rotation change (in degrees) required to trigger the rotation change event. /// [Tooltip("Minimum rotation change (in degrees) required to trigger events")] public float rotationThreshold = 1.0f; /// /// Minimum scale change required to trigger the scale change event. /// [Tooltip("Minimum scale change required to trigger events")] public float scaleThreshold = 0.1f; /// /// The most recent position value that exceeded the threshold. /// private Vector3 _lastPosition; /// /// The most recent scale value that exceeded the threshold. /// private Vector3 _lastScale; /// /// The most recent rotation value that exceeded the threshold. /// private Vector3 _lastRotation; /// /// Gets the last position that exceeded the threshold. /// public Vector3 LastPosition => _lastPosition; /// /// Gets the last scale that exceeded the threshold. /// public Vector3 LastScale => _lastScale; /// /// Gets the last rotation that exceeded the threshold. /// public Vector3 LastRotation => _lastRotation; /// /// Gets the current position of the transform. /// public Vector3 CurrentPosition => transform.position; /// /// Gets the current local scale of the transform. /// public Vector3 CurrentScale => transform.localScale; /// /// Gets the current rotation of the transform in Euler angles. /// public Vector3 CurrentRotation => transform.eulerAngles; /// /// Gets or sets the position from the previous frame. /// public Vector3 PreviousPosition { get; protected set; } = Vector3.zero; /// /// Gets or sets the scale from the previous frame. /// public Vector3 PreviousScale { get; protected set; } = Vector3.zero; /// /// Gets or sets the rotation from the previous frame in Euler angles. /// public Vector3 PreviousRotation { get; protected set; } = Vector3.zero; /// /// Event triggered when position changes exceed the defined threshold. /// Provides the old and new position values. /// [Tooltip("Event triggered when position changes exceed the threshold")] public UnityEvent onChangedPosition = new UnityEvent(); /// /// Event triggered when scale changes exceed the defined threshold. /// Provides the old and new scale values. /// [Tooltip("Event triggered when scale changes exceed the threshold")] public UnityEvent onChangedScale = new UnityEvent(); /// /// Event triggered when rotation changes exceed the defined threshold. /// Provides the old and new rotation values in Euler angles. /// [Tooltip("Event triggered when rotation changes exceed the threshold")] public UnityEvent onChangedRotation = new UnityEvent(); /// /// Detects if a Vector3 value has changed beyond the specified threshold. /// /// Reference to the current value being tracked. /// The new value to compare against. /// The minimum distance required to register a change. /// True if the change exceeds the threshold, false otherwise. private bool DetectChange(ref Vector3 curValue, Vector3 newValue, float threshold) { // Calculate the distance between the current and new values var dis = Vector3.Distance(newValue, curValue); // If the distance is less than or equal to the threshold, no significant change if (dis <= threshold) return false; // Update the current value to the new value curValue = newValue; // Return true to indicate a significant change was detected return true; } /// /// Monitors transform changes every frame and triggers events when changes exceed thresholds. /// private void Update() { // Get the current transform values var pos = transform.position; var scale = transform.localScale; var rotation = transform.eulerAngles; // Check for position changes and trigger events if needed if (!ignore.position && DetectChange(ref _lastPosition, pos, positionThreshold)) { onChangedPosition?.Invoke(new TransformChange() { NewValue = pos, OldValue = _lastPosition }); } // Check for rotation changes and trigger events if needed if (!ignore.rotation && DetectChange(ref _lastRotation, rotation, rotationThreshold)) { onChangedRotation?.Invoke(new TransformChange() { NewValue = rotation, OldValue = _lastRotation }); } // Check for scale changes and trigger events if needed if (!ignore.scale && DetectChange(ref _lastScale, scale, scaleThreshold)) { onChangedScale?.Invoke(new TransformChange() { NewValue = scale, OldValue = _lastScale }); } // Update the previous frame values PreviousPosition = pos; PreviousScale = scale; PreviousRotation = rotation; } } }