123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- // Applies HistoryBounds to the physics world by projecting to a trigger Collider.
- // This way we can use Physics.Raycast on it.
- using UnityEngine;
- namespace Mirror
- {
- public class HistoryCollider : MonoBehaviour
- {
- [Header("Components")]
- [Tooltip("The object's actual collider. We need to know where it is, and how large it is.")]
- public Collider actualCollider;
- [Tooltip("The helper collider that the history bounds are projected onto.\nNeeds to be added to a child GameObject to counter-rotate an axis aligned Bounding Box onto it.\nThis is only used by this component.")]
- public BoxCollider boundsCollider;
- [Header("History")]
- [Tooltip("Keep this many past bounds in the buffer. The larger this is, the further we can raycast into the past.\nMaximum time := historyAmount * captureInterval")]
- public int boundsLimit = 8;
- [Tooltip("Gather N bounds at a time into a bucket for faster encapsulation. A factor of 2 will be twice as fast, etc.")]
- public int boundsPerBucket = 2;
- [Tooltip("Capture bounds every 'captureInterval' seconds. Larger values will require fewer computations, but may not capture every small move.")]
- public float captureInterval = 0.100f; // 100 ms
- double lastCaptureTime = 0;
- [Header("Debug")]
- public Color historyColor = new Color(1.0f, 0.5f, 0.0f, 1.0f);
- public Color currentColor = Color.red;
- protected HistoryBounds history = null;
- protected virtual void Awake()
- {
- history = new HistoryBounds(boundsLimit, boundsPerBucket);
- // ensure colliders were set.
- // bounds collider should always be a trigger.
- if (actualCollider == null) Debug.LogError("HistoryCollider: actualCollider was not set.");
- if (boundsCollider == null) Debug.LogError("HistoryCollider: boundsCollider was not set.");
- if (boundsCollider.transform.parent != transform) Debug.LogError("HistoryCollider: boundsCollider must be a child of this GameObject.");
- if (!boundsCollider.isTrigger) Debug.LogError("HistoryCollider: boundsCollider must be a trigger.");
- }
- // capturing and projecting onto colliders should use physics update
- protected virtual void FixedUpdate()
- {
- // capture current bounds every interval
- if (NetworkTime.localTime >= lastCaptureTime + captureInterval)
- {
- lastCaptureTime = NetworkTime.localTime;
- CaptureBounds();
- }
- // project bounds onto helper collider
- ProjectBounds();
- }
- protected virtual void CaptureBounds()
- {
- // grab current collider bounds
- // this is in world space coordinates, and axis aligned
- // TODO double check
- Bounds bounds = actualCollider.bounds;
- // insert into history
- history.Insert(bounds);
- }
- protected virtual void ProjectBounds()
- {
- // grab total collider encapsulating all of history
- Bounds total = history.total;
- // don't assign empty bounds, this will throw a Unity warning
- if (history.boundsCount == 0) return;
- // scale projection doesn't work yet.
- // for now, don't allow scale changes.
- if (transform.lossyScale != Vector3.one)
- {
- Debug.LogWarning($"HistoryCollider: {name}'s transform global scale must be (1,1,1).");
- return;
- }
- // counter rotate the child collider against the gameobject's rotation.
- // we need this to always be axis aligned.
- boundsCollider.transform.localRotation = Quaternion.Inverse(transform.rotation);
- // project world space bounds to collider's local space
- boundsCollider.center = boundsCollider.transform.InverseTransformPoint(total.center);
- boundsCollider.size = total.size; // TODO projection?
- }
- // TODO runtime drawing for debugging?
- protected virtual void OnDrawGizmos()
- {
- // draw total bounds
- Gizmos.color = historyColor;
- Gizmos.DrawWireCube(history.total.center, history.total.size);
- // draw current bounds
- Gizmos.color = currentColor;
- Gizmos.DrawWireCube(actualCollider.bounds.center, actualCollider.bounds.size);
- }
- }
- }
|