| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- #if !UNITY_2019_3_OR_NEWER
- #define CINEMACHINE_PHYSICS
- #define CINEMACHINE_PHYSICS_2D
- #endif
- using UnityEngine;
- namespace Cinemachine
- {
- /// <summary>An ad-hoc collection of helpers, used by Cinemachine
- /// or its editor tools in various places</summary>
- [DocumentationSorting(DocumentationSortingAttribute.Level.Undoc)]
- public static class RuntimeUtility
- {
- /// <summary>Convenience to destroy an object, using the appropriate method depending
- /// on whether the game is playing</summary>
- /// <param name="obj">The object to destroy</param>
- public static void DestroyObject(UnityEngine.Object obj)
- {
- if (obj != null)
- {
- #if UNITY_EDITOR
- if (Application.isPlaying)
- UnityEngine.Object.Destroy(obj);
- else
- UnityEngine.Object.DestroyImmediate(obj);
- #else
- UnityEngine.Object.Destroy(obj);
- #endif
- }
- }
- /// <summary>
- /// Check whether a GameObject is a prefab.
- /// For editor only - some things are disallowed if prefab. In runtime, will always return false.
- /// </summary>
- /// <param name="gameObject">the object to check</param>
- /// <returns>If editor, checks if object is a prefab or prefab instance.
- /// In runtime, returns false always</returns>
- public static bool IsPrefab(GameObject gameObject)
- {
- #if UNITY_EDITOR
- return UnityEditor.PrefabUtility.GetPrefabInstanceStatus(gameObject)
- != UnityEditor.PrefabInstanceStatus.NotAPrefab;
- #else
- return false;
- #endif
- }
-
- #if CINEMACHINE_PHYSICS
- private static RaycastHit[] s_HitBuffer = new RaycastHit[16];
- private static int[] s_PenetrationIndexBuffer = new int[16];
- /// <summary>
- /// Perform a raycast, but pass through any objects that have a given tag
- /// </summary>
- /// <param name="ray">The ray to cast</param>
- /// <param name="hitInfo">The returned results</param>
- /// <param name="rayLength">Length of the raycast</param>
- /// <param name="layerMask">Layers to include</param>
- /// <param name="ignoreTag">Tag to ignore</param>
- /// <returns>True if something was hit. Results in hitInfo</returns>
- public static bool RaycastIgnoreTag(
- Ray ray, out RaycastHit hitInfo, float rayLength, int layerMask, in string ignoreTag)
- {
- if (ignoreTag.Length == 0)
- {
- if (Physics.Raycast(
- ray, out hitInfo, rayLength, layerMask,
- QueryTriggerInteraction.Ignore))
- {
- return true;
- }
- }
- else
- {
- int closestHit = -1;
- int numHits = Physics.RaycastNonAlloc(
- ray, s_HitBuffer, rayLength, layerMask, QueryTriggerInteraction.Ignore);
- for (int i = 0; i < numHits; ++i)
- {
- if (s_HitBuffer[i].collider.CompareTag(ignoreTag))
- continue;
- if (closestHit < 0 || s_HitBuffer[i].distance < s_HitBuffer[closestHit].distance)
- closestHit = i;
- }
- if (closestHit >= 0)
- {
- hitInfo = s_HitBuffer[closestHit];
- if (numHits == s_HitBuffer.Length)
- s_HitBuffer = new RaycastHit[s_HitBuffer.Length * 2]; // full! grow for next time
- return true;
- }
- }
- hitInfo = new RaycastHit();
- return false;
- }
- /// <summary>
- /// Perform a sphere cast, but pass through objects with a given tag
- /// </summary>
- /// <param name="rayStart">Start of the ray</param>
- /// <param name="radius">Radius of the sphere cast</param>
- /// <param name="dir">Normalized direction of the ray</param>
- /// <param name="hitInfo">Results go here</param>
- /// <param name="rayLength">Length of the ray</param>
- /// <param name="layerMask">Layers to include</param>
- /// <param name="ignoreTag">Tag to ignore</param>
- /// <returns>True if something is hit. Results in hitInfo.</returns>
- public static bool SphereCastIgnoreTag(
- Vector3 rayStart, float radius, Vector3 dir,
- out RaycastHit hitInfo, float rayLength,
- int layerMask, in string ignoreTag)
- {
- int closestHit = -1;
- int numPenetrations = 0;
- float penetrationDistanceSum = 0;
- int numHits = Physics.SphereCastNonAlloc(
- rayStart, radius, dir, s_HitBuffer, rayLength, layerMask,
- QueryTriggerInteraction.Ignore);
- for (int i = 0; i < numHits; ++i)
- {
- var h = s_HitBuffer[i];
- if (ignoreTag.Length > 0 && h.collider.CompareTag(ignoreTag))
- continue;
- // Collect overlapping items
- if (h.distance == 0 && h.normal == -dir)
- {
- if (s_PenetrationIndexBuffer.Length > numPenetrations + 1)
- s_PenetrationIndexBuffer[numPenetrations++] = i;
- // hitInfo for overlapping colliders will have special
- // values that are not helpful to the caller. Fix that here.
- var scratchCollider = GetScratchCollider();
- scratchCollider.radius = radius;
- var c = h.collider;
- if (Physics.ComputePenetration(
- scratchCollider, rayStart, Quaternion.identity,
- c, c.transform.position, c.transform.rotation,
- out var offsetDir, out var offsetDistance))
- {
- h.point = rayStart + offsetDir * (offsetDistance - radius);
- h.distance = offsetDistance - radius; // will be -ve
- h.normal = offsetDir;
- s_HitBuffer[i] = h;
- penetrationDistanceSum += h.distance;
- }
- else
- {
- continue; // don't know what's going on, just forget about it
- }
- }
- if (closestHit < 0 || h.distance < s_HitBuffer[closestHit].distance)
- {
- closestHit = i;
- }
- }
- // Naively combine penetrating items
- if (numPenetrations > 1)
- {
- hitInfo = new RaycastHit();
- for (int i = 0; i < numPenetrations; ++i)
- {
- var h = s_HitBuffer[s_PenetrationIndexBuffer[i]];
- var t = h.distance / penetrationDistanceSum;
- hitInfo.point += h.point * t;
- hitInfo.distance += h.distance * t;
- hitInfo.normal += h.normal * t;
- }
- hitInfo.normal = hitInfo.normal.normalized;
- return true;
- }
- if (closestHit >= 0)
- {
- hitInfo = s_HitBuffer[closestHit];
- if (numHits == s_HitBuffer.Length)
- s_HitBuffer = new RaycastHit[s_HitBuffer.Length * 2]; // full! grow for next time
- return true;
- }
- hitInfo = new RaycastHit();
- return false;
- }
- private static SphereCollider s_ScratchCollider;
- private static GameObject s_ScratchColliderGameObject;
- internal static SphereCollider GetScratchCollider()
- {
- if (s_ScratchColliderGameObject == null)
- {
- s_ScratchColliderGameObject = new GameObject("Cinemachine Scratch Collider");
- s_ScratchColliderGameObject.hideFlags = HideFlags.HideAndDontSave;
- s_ScratchColliderGameObject.transform.position = Vector3.zero;
- s_ScratchColliderGameObject.SetActive(true);
- s_ScratchCollider = s_ScratchColliderGameObject.AddComponent<SphereCollider>();
- s_ScratchCollider.isTrigger = true;
- var rb = s_ScratchColliderGameObject.AddComponent<Rigidbody>();
- rb.detectCollisions = false;
- rb.isKinematic = true;
- }
- return s_ScratchCollider;
- }
- internal static void DestroyScratchCollider()
- {
- if (s_ScratchColliderGameObject != null)
- {
- s_ScratchColliderGameObject.SetActive(false);
- DestroyObject(s_ScratchColliderGameObject.GetComponent<Rigidbody>());
- }
- DestroyObject(s_ScratchCollider);
- DestroyObject(s_ScratchColliderGameObject);
- s_ScratchColliderGameObject = null;
- s_ScratchCollider = null;
- }
- #endif
- }
- }
|