ProtectCameraFromWallClip.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using System;
  2. using System.Collections;
  3. using UnityEngine;
  4. namespace UnityStandardAssets.Cameras
  5. {
  6. public class ProtectCameraFromWallClip : MonoBehaviour
  7. {
  8. public float clipMoveTime = 0.05f; // time taken to move when avoiding cliping (low value = fast, which it should be)
  9. public float returnTime = 0.4f; // time taken to move back towards desired position, when not clipping (typically should be a higher value than clipMoveTime)
  10. public float sphereCastRadius = 0.1f; // the radius of the sphere used to test for object between camera and target
  11. public bool visualiseInEditor; // toggle for visualising the algorithm through lines for the raycast in the editor
  12. public float closestDistance = 0.5f; // the closest distance the camera can be from the target
  13. public bool protecting { get; private set; } // used for determining if there is an object between the target and the camera
  14. public string dontClipTag = "Player"; // don't clip against objects with this tag (useful for not clipping against the targeted object)
  15. private Transform m_Cam; // the transform of the camera
  16. private Transform m_Pivot; // the point at which the camera pivots around
  17. private float m_OriginalDist; // the original distance to the camera before any modification are made
  18. private float m_MoveVelocity; // the velocity at which the camera moved
  19. private float m_CurrentDist; // the current distance from the camera to the target
  20. private Ray m_Ray = new Ray(); // the ray used in the lateupdate for casting between the camera and the target
  21. private RaycastHit[] m_Hits; // the hits between the camera and the target
  22. private RayHitComparer m_RayHitComparer; // variable to compare raycast hit distances
  23. private void Start()
  24. {
  25. // find the camera in the object hierarchy
  26. m_Cam = GetComponentInChildren<Camera>().transform;
  27. m_Pivot = m_Cam.parent;
  28. m_OriginalDist = m_Cam.localPosition.magnitude;
  29. m_CurrentDist = m_OriginalDist;
  30. // create a new RayHitComparer
  31. m_RayHitComparer = new RayHitComparer();
  32. }
  33. private void LateUpdate()
  34. {
  35. // initially set the target distance
  36. float targetDist = m_OriginalDist;
  37. m_Ray.origin = m_Pivot.position + m_Pivot.forward*sphereCastRadius;
  38. m_Ray.direction = -m_Pivot.forward;
  39. // initial check to see if start of spherecast intersects anything
  40. var cols = Physics.OverlapSphere(m_Ray.origin, sphereCastRadius);
  41. bool initialIntersect = false;
  42. bool hitSomething = false;
  43. // loop through all the collisions to check if something we care about
  44. for (int i = 0; i < cols.Length; i++)
  45. {
  46. if ((!cols[i].isTrigger) &&
  47. !(cols[i].attachedRigidbody != null && cols[i].attachedRigidbody.CompareTag(dontClipTag)))
  48. {
  49. initialIntersect = true;
  50. break;
  51. }
  52. }
  53. // if there is a collision
  54. if (initialIntersect)
  55. {
  56. m_Ray.origin += m_Pivot.forward*sphereCastRadius;
  57. // do a raycast and gather all the intersections
  58. m_Hits = Physics.RaycastAll(m_Ray, m_OriginalDist - sphereCastRadius);
  59. }
  60. else
  61. {
  62. // if there was no collision do a sphere cast to see if there were any other collisions
  63. m_Hits = Physics.SphereCastAll(m_Ray, sphereCastRadius, m_OriginalDist + sphereCastRadius);
  64. }
  65. // sort the collisions by distance
  66. Array.Sort(m_Hits, m_RayHitComparer);
  67. // set the variable used for storing the closest to be as far as possible
  68. float nearest = Mathf.Infinity;
  69. // loop through all the collisions
  70. for (int i = 0; i < m_Hits.Length; i++)
  71. {
  72. // only deal with the collision if it was closer than the previous one, not a trigger, and not attached to a rigidbody tagged with the dontClipTag
  73. if (m_Hits[i].distance < nearest && (!m_Hits[i].collider.isTrigger) &&
  74. !(m_Hits[i].collider.attachedRigidbody != null &&
  75. m_Hits[i].collider.attachedRigidbody.CompareTag(dontClipTag)))
  76. {
  77. // change the nearest collision to latest
  78. nearest = m_Hits[i].distance;
  79. targetDist = -m_Pivot.InverseTransformPoint(m_Hits[i].point).z;
  80. hitSomething = true;
  81. }
  82. }
  83. // visualise the cam clip effect in the editor
  84. if (hitSomething)
  85. {
  86. Debug.DrawRay(m_Ray.origin, -m_Pivot.forward*(targetDist + sphereCastRadius), Color.red);
  87. }
  88. // hit something so move the camera to a better position
  89. protecting = hitSomething;
  90. m_CurrentDist = Mathf.SmoothDamp(m_CurrentDist, targetDist, ref m_MoveVelocity,
  91. m_CurrentDist > targetDist ? clipMoveTime : returnTime);
  92. m_CurrentDist = Mathf.Clamp(m_CurrentDist, closestDistance, m_OriginalDist);
  93. m_Cam.localPosition = -Vector3.forward*m_CurrentDist;
  94. }
  95. // comparer for check distances in ray cast hits
  96. public class RayHitComparer : IComparer
  97. {
  98. public int Compare(object x, object y)
  99. {
  100. return ((RaycastHit) x).distance.CompareTo(((RaycastHit) y).distance);
  101. }
  102. }
  103. }
  104. }