CinemachineCollider.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. #if !UNITY_2019_3_OR_NEWER
  2. #define CINEMACHINE_PHYSICS
  3. #define CINEMACHINE_PHYSICS_2D
  4. #endif
  5. using UnityEngine;
  6. using System.Collections.Generic;
  7. using Cinemachine.Utility;
  8. using UnityEngine.Serialization;
  9. using System;
  10. using UnityEngine.SceneManagement;
  11. namespace Cinemachine
  12. {
  13. #if CINEMACHINE_PHYSICS
  14. /// <summary>
  15. /// An add-on module for Cinemachine Virtual Camera that post-processes
  16. /// the final position of the virtual camera. Based on the supplied settings,
  17. /// the Collider will attempt to preserve the line of sight
  18. /// with the LookAt target of the virtual camera by moving
  19. /// away from objects that will obstruct the view.
  20. ///
  21. /// Additionally, the Collider can be used to assess the shot quality and
  22. /// report this as a field in the camera State.
  23. /// </summary>
  24. [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
  25. [AddComponentMenu("")] // Hide in menu
  26. [SaveDuringPlay]
  27. #if UNITY_2018_3_OR_NEWER
  28. [ExecuteAlways]
  29. #else
  30. [ExecuteInEditMode]
  31. #endif
  32. [DisallowMultipleComponent]
  33. [HelpURL(Documentation.BaseURL + "manual/CinemachineCollider.html")]
  34. public class CinemachineCollider : CinemachineExtension
  35. {
  36. /// <summary>Objects on these layers will be detected.</summary>
  37. [Header("Obstacle Detection")]
  38. [Tooltip("Objects on these layers will be detected")]
  39. public LayerMask m_CollideAgainst = 1;
  40. /// <summary>Obstacles with this tag will be ignored. It is a good idea to set this field to the target's tag</summary>
  41. [TagField]
  42. [Tooltip("Obstacles with this tag will be ignored. It is a good idea to set this field to the target's tag")]
  43. public string m_IgnoreTag = string.Empty;
  44. /// <summary>Objects on these layers will never obstruct view of the target.</summary>
  45. [Tooltip("Objects on these layers will never obstruct view of the target")]
  46. public LayerMask m_TransparentLayers = 0;
  47. /// <summary>Obstacles closer to the target than this will be ignored</summary>
  48. [Tooltip("Obstacles closer to the target than this will be ignored")]
  49. public float m_MinimumDistanceFromTarget = 0.1f;
  50. /// <summary>
  51. /// When enabled, will attempt to resolve situations where the line of sight to the
  52. /// target is blocked by an obstacle
  53. /// </summary>
  54. [Space]
  55. [Tooltip("When enabled, will attempt to resolve situations where the line of sight to the target is blocked by an obstacle")]
  56. [FormerlySerializedAs("m_PreserveLineOfSight")]
  57. public bool m_AvoidObstacles = true;
  58. /// <summary>
  59. /// The raycast distance to test for when checking if the line of sight to this camera's target is clear.
  60. /// </summary>
  61. [Tooltip("The maximum raycast distance when checking if the line of sight to this camera's target is clear. If the setting is 0 or less, the current actual distance to target will be used.")]
  62. [FormerlySerializedAs("m_LineOfSightFeelerDistance")]
  63. public float m_DistanceLimit = 0f;
  64. /// <summary>
  65. /// Don't take action unless occlusion has lasted at least this long.
  66. /// </summary>
  67. [Tooltip("Don't take action unless occlusion has lasted at least this long.")]
  68. public float m_MinimumOcclusionTime = 0f;
  69. /// <summary>
  70. /// Camera will try to maintain this distance from any obstacle.
  71. /// Increase this value if you are seeing inside obstacles due to a large
  72. /// FOV on the camera.
  73. /// </summary>
  74. [Tooltip("Camera will try to maintain this distance from any obstacle. Try to keep this value small. Increase it if you are seeing inside obstacles due to a large FOV on the camera.")]
  75. public float m_CameraRadius = 0.1f;
  76. /// <summary>The way in which the Collider will attempt to preserve sight of the target.</summary>
  77. public enum ResolutionStrategy
  78. {
  79. /// <summary>Camera will be pulled forward along its Z axis until it is in front of
  80. /// the nearest obstacle</summary>
  81. PullCameraForward,
  82. /// <summary>In addition to pulling the camera forward, an effort will be made to
  83. /// return the camera to its original height</summary>
  84. PreserveCameraHeight,
  85. /// <summary>In addition to pulling the camera forward, an effort will be made to
  86. /// return the camera to its original distance from the target</summary>
  87. PreserveCameraDistance
  88. };
  89. /// <summary>The way in which the Collider will attempt to preserve sight of the target.</summary>
  90. [Tooltip("The way in which the Collider will attempt to preserve sight of the target.")]
  91. public ResolutionStrategy m_Strategy = ResolutionStrategy.PreserveCameraHeight;
  92. /// <summary>
  93. /// Upper limit on how many obstacle hits to process. Higher numbers may impact performance.
  94. /// In most environments, 4 is enough.
  95. /// </summary>
  96. [Range(1, 10)]
  97. [Tooltip("Upper limit on how many obstacle hits to process. Higher numbers may impact performance. In most environments, 4 is enough.")]
  98. public int m_MaximumEffort = 4;
  99. /// <summary>
  100. /// Smoothing to apply to obstruction resolution. Nearest camera point is held for at least this long.
  101. /// </summary>
  102. [Range(0, 2)]
  103. [Tooltip("Smoothing to apply to obstruction resolution. Nearest camera point is held for at least this long")]
  104. public float m_SmoothingTime = 0;
  105. /// <summary>
  106. /// How gradually the camera returns to its normal position after having been corrected.
  107. /// Higher numbers will move the camera more gradually back to normal.
  108. /// </summary>
  109. [Range(0, 10)]
  110. [Tooltip("How gradually the camera returns to its normal position after having been corrected. Higher numbers will move the camera more gradually back to normal.")]
  111. [FormerlySerializedAs("m_Smoothing")]
  112. public float m_Damping = 0;
  113. /// <summary>
  114. /// How gradually the camera moves to resolve an occlusion.
  115. /// Higher numbers will move the camera more gradually.
  116. /// </summary>
  117. [Range(0, 10)]
  118. [Tooltip("How gradually the camera moves to resolve an occlusion. Higher numbers will move the camera more gradually.")]
  119. public float m_DampingWhenOccluded = 0;
  120. /// <summary>If greater than zero, a higher score will be given to shots when the target is closer to
  121. /// this distance. Set this to zero to disable this feature</summary>
  122. [Header("Shot Evaluation")]
  123. [Tooltip("If greater than zero, a higher score will be given to shots when the target is closer to this distance. Set this to zero to disable this feature.")]
  124. public float m_OptimalTargetDistance = 0;
  125. /// <summary>See wheter an object is blocking the camera's view of the target</summary>
  126. /// <param name="vcam">The virtual camera in question. This might be different from the
  127. /// virtual camera that owns the collider, in the event that the camera has children</param>
  128. /// <returns>True if something is blocking the view</returns>
  129. public bool IsTargetObscured(ICinemachineCamera vcam)
  130. {
  131. return GetExtraState<VcamExtraState>(vcam).targetObscured;
  132. }
  133. /// <summary>See whether the virtual camera has been moved nby the collider</summary>
  134. /// <param name="vcam">The virtual camera in question. This might be different from the
  135. /// virtual camera that owns the collider, in the event that the camera has children</param>
  136. /// <returns>True if the virtual camera has been displaced due to collision or
  137. /// target obstruction</returns>
  138. public bool CameraWasDisplaced(ICinemachineCamera vcam)
  139. {
  140. return GetCameraDisplacementDistance(vcam) > 0;
  141. }
  142. /// <summary>See how far the virtual camera wa moved nby the collider</summary>
  143. /// <param name="vcam">The virtual camera in question. This might be different from the
  144. /// virtual camera that owns the collider, in the event that the camera has children</param>
  145. /// <returns>True if the virtual camera has been displaced due to collision or
  146. /// target obstruction</returns>
  147. public float GetCameraDisplacementDistance(ICinemachineCamera vcam)
  148. {
  149. return GetExtraState<VcamExtraState>(vcam).colliderDisplacement;
  150. }
  151. private void OnValidate()
  152. {
  153. m_DistanceLimit = Mathf.Max(0, m_DistanceLimit);
  154. m_MinimumOcclusionTime = Mathf.Max(0, m_MinimumOcclusionTime);
  155. m_CameraRadius = Mathf.Max(0, m_CameraRadius);
  156. m_MinimumDistanceFromTarget = Mathf.Max(0.01f, m_MinimumDistanceFromTarget);
  157. m_OptimalTargetDistance = Mathf.Max(0, m_OptimalTargetDistance);
  158. }
  159. /// <summary>
  160. /// Cleanup
  161. /// </summary>
  162. protected override void OnDestroy()
  163. {
  164. DestroyCollider();
  165. base.OnDestroy();
  166. }
  167. /// This must be small but greater than 0 - reduces false results due to precision
  168. const float PrecisionSlush = 0.001f;
  169. /// <summary>
  170. /// Per-vcam extra state info
  171. /// </summary>
  172. class VcamExtraState
  173. {
  174. public Vector3 m_previousDisplacement;
  175. public Vector3 m_previousDisplacementCorrection;
  176. public float colliderDisplacement;
  177. public bool targetObscured;
  178. public float occlusionStartTime;
  179. public List<Vector3> debugResolutionPath = null;
  180. public void AddPointToDebugPath(Vector3 p)
  181. {
  182. #if UNITY_EDITOR
  183. if (debugResolutionPath == null)
  184. debugResolutionPath = new List<Vector3>();
  185. debugResolutionPath.Add(p);
  186. #endif
  187. }
  188. // Thanks to Sebastien LeTouze from Exiin Studio for the smoothing idea
  189. private float m_SmoothedDistance;
  190. private float m_SmoothedTime;
  191. public float ApplyDistanceSmoothing(float distance, float smoothingTime)
  192. {
  193. if (m_SmoothedTime != 0 && smoothingTime > Epsilon)
  194. {
  195. float now = CinemachineCore.CurrentTime;
  196. if (now - m_SmoothedTime < smoothingTime)
  197. return Mathf.Min(distance, m_SmoothedDistance);
  198. }
  199. return distance;
  200. }
  201. public void UpdateDistanceSmoothing(float distance, float smoothingTime)
  202. {
  203. float now = CinemachineCore.CurrentTime;
  204. if (m_SmoothedDistance == 0 || distance <= m_SmoothedDistance)
  205. {
  206. m_SmoothedDistance = distance;
  207. m_SmoothedTime = now;
  208. }
  209. }
  210. public void ResetDistanceSmoothing(float smoothingTime)
  211. {
  212. float now = CinemachineCore.CurrentTime;
  213. if (now - m_SmoothedTime >= smoothingTime)
  214. m_SmoothedDistance = m_SmoothedTime = 0;
  215. }
  216. };
  217. /// <summary>Inspector API for debugging collision resolution path</summary>
  218. public List<List<Vector3>> DebugPaths
  219. {
  220. get
  221. {
  222. List<List<Vector3>> list = new List<List<Vector3>>();
  223. List<VcamExtraState> extraStates = GetAllExtraStates<VcamExtraState>();
  224. foreach (var v in extraStates)
  225. if (v.debugResolutionPath != null && v.debugResolutionPath.Count > 0)
  226. list.Add(v.debugResolutionPath);
  227. return list;
  228. }
  229. }
  230. /// <summary>
  231. /// Report maximum damping time needed for this component.
  232. /// </summary>
  233. /// <returns>Highest damping setting in this component</returns>
  234. public override float GetMaxDampTime()
  235. {
  236. return Mathf.Max(m_Damping, Mathf.Max(m_DampingWhenOccluded, m_SmoothingTime));
  237. }
  238. /// <summary>
  239. /// Callback to do the collision resolution and shot evaluation
  240. /// </summary>
  241. /// <param name="vcam">The virtual camera being processed</param>
  242. /// <param name="stage">The current pipeline stage</param>
  243. /// <param name="state">The current virtual camera state</param>
  244. /// <param name="deltaTime">The current applicable deltaTime</param>
  245. protected override void PostPipelineStageCallback(
  246. CinemachineVirtualCameraBase vcam,
  247. CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
  248. {
  249. VcamExtraState extra = null;
  250. if (stage == CinemachineCore.Stage.Body)
  251. {
  252. extra = GetExtraState<VcamExtraState>(vcam);
  253. extra.targetObscured = false;
  254. extra.colliderDisplacement = 0;
  255. if (extra.debugResolutionPath != null)
  256. extra.debugResolutionPath.RemoveRange(0, extra.debugResolutionPath.Count);
  257. }
  258. // Move the body before the Aim is calculated
  259. if (stage == CinemachineCore.Stage.Body)
  260. {
  261. if (m_AvoidObstacles)
  262. {
  263. Vector3 displacement = Vector3.zero;
  264. displacement = PreserveLineOfSight(ref state, ref extra);
  265. extra.m_previousDisplacement =
  266. Quaternion.Euler(state.PositionDampingBypass) * extra.m_previousDisplacement;
  267. if (m_MinimumOcclusionTime > Epsilon)
  268. {
  269. float now = CinemachineCore.CurrentTime;
  270. if (displacement.sqrMagnitude < Epsilon)
  271. extra.occlusionStartTime = 0;
  272. else
  273. {
  274. if (extra.occlusionStartTime <= 0)
  275. extra.occlusionStartTime = now;
  276. if (now - extra.occlusionStartTime < m_MinimumOcclusionTime)
  277. displacement = extra.m_previousDisplacement;
  278. }
  279. }
  280. // Apply distance smoothing
  281. if (m_SmoothingTime > Epsilon)
  282. {
  283. Vector3 pos = state.CorrectedPosition + displacement;
  284. Vector3 dir = pos - state.ReferenceLookAt;
  285. float distance = dir.magnitude;
  286. if (distance > Epsilon)
  287. {
  288. dir /= distance;
  289. if (!displacement.AlmostZero())
  290. extra.UpdateDistanceSmoothing(distance, m_SmoothingTime);
  291. distance = extra.ApplyDistanceSmoothing(distance, m_SmoothingTime);
  292. displacement += (state.ReferenceLookAt + dir * distance) - pos;
  293. }
  294. }
  295. float damping = m_Damping;
  296. if (displacement.AlmostZero())
  297. extra.ResetDistanceSmoothing(m_SmoothingTime);
  298. else
  299. damping = m_DampingWhenOccluded;
  300. if (damping > 0 && deltaTime >= 0 && VirtualCamera.PreviousStateIsValid)
  301. {
  302. Vector3 delta = displacement - extra.m_previousDisplacement;
  303. delta = Damper.Damp(delta, damping, deltaTime);
  304. displacement = extra.m_previousDisplacement + delta;
  305. }
  306. extra.m_previousDisplacement = displacement;
  307. Vector3 correction = RespectCameraRadius(state.CorrectedPosition + displacement, ref state);
  308. if (damping > 0 && deltaTime >= 0 && VirtualCamera.PreviousStateIsValid)
  309. {
  310. Vector3 delta = correction - extra.m_previousDisplacementCorrection;
  311. delta = Damper.Damp(delta, damping, deltaTime);
  312. correction = extra.m_previousDisplacementCorrection + delta;
  313. }
  314. displacement += correction;
  315. extra.m_previousDisplacementCorrection = correction;
  316. state.PositionCorrection += displacement;
  317. extra.colliderDisplacement += displacement.magnitude;
  318. }
  319. }
  320. // Rate the shot after the aim was set
  321. if (stage == CinemachineCore.Stage.Aim)
  322. {
  323. extra = GetExtraState<VcamExtraState>(vcam);
  324. extra.targetObscured = IsTargetOffscreen(state) || CheckForTargetObstructions(state);
  325. // GML these values are an initial arbitrary attempt at rating quality
  326. if (extra.targetObscured)
  327. state.ShotQuality *= 0.2f;
  328. if (extra.colliderDisplacement > 0)
  329. state.ShotQuality *= 0.8f;
  330. float nearnessBoost = 0;
  331. const float kMaxNearBoost = 0.2f;
  332. if (m_OptimalTargetDistance > 0 && state.HasLookAt)
  333. {
  334. float distance = Vector3.Magnitude(state.ReferenceLookAt - state.FinalPosition);
  335. if (distance <= m_OptimalTargetDistance)
  336. {
  337. float threshold = m_OptimalTargetDistance / 2;
  338. if (distance >= threshold)
  339. nearnessBoost = kMaxNearBoost * (distance - threshold)
  340. / (m_OptimalTargetDistance - threshold);
  341. }
  342. else
  343. {
  344. distance -= m_OptimalTargetDistance;
  345. float threshold = m_OptimalTargetDistance * 3;
  346. if (distance < threshold)
  347. nearnessBoost = kMaxNearBoost * (1f - (distance / threshold));
  348. }
  349. state.ShotQuality *= (1f + nearnessBoost);
  350. }
  351. }
  352. }
  353. private Vector3 PreserveLineOfSight(ref CameraState state, ref VcamExtraState extra)
  354. {
  355. Vector3 displacement = Vector3.zero;
  356. if (state.HasLookAt && m_CollideAgainst != 0
  357. && m_CollideAgainst != m_TransparentLayers)
  358. {
  359. Vector3 cameraPos = state.CorrectedPosition;
  360. Vector3 lookAtPos = state.ReferenceLookAt;
  361. RaycastHit hitInfo = new RaycastHit();
  362. displacement = PullCameraInFrontOfNearestObstacle(
  363. cameraPos, lookAtPos, m_CollideAgainst & ~m_TransparentLayers, ref hitInfo);
  364. Vector3 pos = cameraPos + displacement;
  365. if (hitInfo.collider != null)
  366. {
  367. extra.AddPointToDebugPath(pos);
  368. if (m_Strategy != ResolutionStrategy.PullCameraForward)
  369. {
  370. Vector3 targetToCamera = cameraPos - lookAtPos;
  371. pos = PushCameraBack(
  372. pos, targetToCamera, hitInfo, lookAtPos,
  373. new Plane(state.ReferenceUp, cameraPos),
  374. targetToCamera.magnitude, m_MaximumEffort, ref extra);
  375. }
  376. }
  377. displacement = pos - cameraPos;
  378. }
  379. return displacement;
  380. }
  381. private Vector3 PullCameraInFrontOfNearestObstacle(
  382. Vector3 cameraPos, Vector3 lookAtPos, int layerMask, ref RaycastHit hitInfo)
  383. {
  384. Vector3 displacement = Vector3.zero;
  385. Vector3 dir = cameraPos - lookAtPos;
  386. float targetDistance = dir.magnitude;
  387. if (targetDistance > Epsilon)
  388. {
  389. dir /= targetDistance;
  390. float minDistanceFromTarget = Mathf.Max(m_MinimumDistanceFromTarget, Epsilon);
  391. if (targetDistance < minDistanceFromTarget + Epsilon)
  392. displacement = dir * (minDistanceFromTarget - targetDistance);
  393. else
  394. {
  395. float rayLength = targetDistance - minDistanceFromTarget;
  396. if (m_DistanceLimit > Epsilon)
  397. rayLength = Mathf.Min(m_DistanceLimit, rayLength);
  398. // Make a ray that looks towards the camera, to get the obstacle closest to target
  399. Ray ray = new Ray(cameraPos - rayLength * dir, dir);
  400. rayLength += PrecisionSlush;
  401. if (rayLength > Epsilon)
  402. {
  403. if (RuntimeUtility.RaycastIgnoreTag(
  404. ray, out hitInfo, rayLength, layerMask, m_IgnoreTag))
  405. {
  406. // Pull camera forward in front of obstacle
  407. float adjustment = Mathf.Max(0, hitInfo.distance - PrecisionSlush);
  408. displacement = ray.GetPoint(adjustment) - cameraPos;
  409. }
  410. }
  411. }
  412. }
  413. return displacement;
  414. }
  415. private Vector3 PushCameraBack(
  416. Vector3 currentPos, Vector3 pushDir, RaycastHit obstacle,
  417. Vector3 lookAtPos, Plane startPlane, float targetDistance, int iterations,
  418. ref VcamExtraState extra)
  419. {
  420. // Take a step along the wall.
  421. Vector3 pos = currentPos;
  422. Vector3 dir = Vector3.zero;
  423. if (!GetWalkingDirection(pos, pushDir, obstacle, ref dir))
  424. return pos;
  425. Ray ray = new Ray(pos, dir);
  426. float distance = GetPushBackDistance(ray, startPlane, targetDistance, lookAtPos);
  427. if (distance <= Epsilon)
  428. return pos;
  429. // Check only as far as the obstacle bounds
  430. float clampedDistance = ClampRayToBounds(ray, distance, obstacle.collider.bounds);
  431. distance = Mathf.Min(distance, clampedDistance + PrecisionSlush);
  432. RaycastHit hitInfo;
  433. if (RuntimeUtility.RaycastIgnoreTag(ray, out hitInfo, distance,
  434. m_CollideAgainst & ~m_TransparentLayers, m_IgnoreTag))
  435. {
  436. // We hit something. Stop there and take a step along that wall.
  437. float adjustment = hitInfo.distance - PrecisionSlush;
  438. pos = ray.GetPoint(adjustment);
  439. extra.AddPointToDebugPath(pos);
  440. if (iterations > 1)
  441. pos = PushCameraBack(
  442. pos, dir, hitInfo,
  443. lookAtPos, startPlane,
  444. targetDistance, iterations-1, ref extra);
  445. return pos;
  446. }
  447. // Didn't hit anything. Can we push back all the way now?
  448. pos = ray.GetPoint(distance);
  449. // First check if we can still see the target. If not, abort
  450. dir = pos - lookAtPos;
  451. float d = dir.magnitude;
  452. RaycastHit hitInfo2;
  453. if (d < Epsilon || RuntimeUtility.RaycastIgnoreTag(
  454. new Ray(lookAtPos, dir), out hitInfo2, d - PrecisionSlush,
  455. m_CollideAgainst & ~m_TransparentLayers, m_IgnoreTag))
  456. return currentPos;
  457. // All clear
  458. ray = new Ray(pos, dir);
  459. extra.AddPointToDebugPath(pos);
  460. distance = GetPushBackDistance(ray, startPlane, targetDistance, lookAtPos);
  461. if (distance > Epsilon)
  462. {
  463. if (!RuntimeUtility.RaycastIgnoreTag(ray, out hitInfo, distance,
  464. m_CollideAgainst & ~m_TransparentLayers, m_IgnoreTag))
  465. {
  466. pos = ray.GetPoint(distance); // no obstacles - all good
  467. extra.AddPointToDebugPath(pos);
  468. }
  469. else
  470. {
  471. // We hit something. Stop there and maybe take a step along that wall
  472. float adjustment = hitInfo.distance - PrecisionSlush;
  473. pos = ray.GetPoint(adjustment);
  474. extra.AddPointToDebugPath(pos);
  475. if (iterations > 1)
  476. pos = PushCameraBack(
  477. pos, dir, hitInfo, lookAtPos, startPlane,
  478. targetDistance, iterations-1, ref extra);
  479. }
  480. }
  481. return pos;
  482. }
  483. private RaycastHit[] m_CornerBuffer = new RaycastHit[4];
  484. private bool GetWalkingDirection(
  485. Vector3 pos, Vector3 pushDir, RaycastHit obstacle, ref Vector3 outDir)
  486. {
  487. Vector3 normal2 = obstacle.normal;
  488. // Check for nearby obstacles. Are we in a corner?
  489. float nearbyDistance = PrecisionSlush * 5;
  490. int numFound = Physics.SphereCastNonAlloc(
  491. pos, nearbyDistance, pushDir.normalized, m_CornerBuffer, 0,
  492. m_CollideAgainst & ~m_TransparentLayers, QueryTriggerInteraction.Ignore);
  493. if (numFound > 1)
  494. {
  495. // Calculate the second normal
  496. for (int i = 0; i < numFound; ++i)
  497. {
  498. if (m_CornerBuffer[i].collider == null)
  499. continue;
  500. if (m_IgnoreTag.Length > 0 && m_CornerBuffer[i].collider.CompareTag(m_IgnoreTag))
  501. continue;
  502. Type type = m_CornerBuffer[i].collider.GetType();
  503. if (type == typeof(BoxCollider)
  504. || type == typeof(SphereCollider)
  505. || type == typeof(CapsuleCollider))
  506. {
  507. Vector3 p = m_CornerBuffer[i].collider.ClosestPoint(pos);
  508. Vector3 d = p - pos;
  509. if (d.magnitude > Vector3.kEpsilon)
  510. {
  511. if (m_CornerBuffer[i].collider.Raycast(
  512. new Ray(pos, d), out m_CornerBuffer[i], nearbyDistance))
  513. {
  514. if (!(m_CornerBuffer[i].normal - obstacle.normal).AlmostZero())
  515. normal2 = m_CornerBuffer[i].normal;
  516. break;
  517. }
  518. }
  519. }
  520. }
  521. }
  522. // Walk along the wall. If we're in a corner, walk their intersecting line
  523. Vector3 dir = Vector3.Cross(obstacle.normal, normal2);
  524. if (dir.AlmostZero())
  525. dir = Vector3.ProjectOnPlane(pushDir, obstacle.normal);
  526. else
  527. {
  528. float dot = Vector3.Dot(dir, pushDir);
  529. if (Mathf.Abs(dot) < Epsilon)
  530. return false;
  531. if (dot < 0)
  532. dir = -dir;
  533. }
  534. if (dir.AlmostZero())
  535. return false;
  536. outDir = dir.normalized;
  537. return true;
  538. }
  539. const float AngleThreshold = 0.1f;
  540. float GetPushBackDistance(Ray ray, Plane startPlane, float targetDistance, Vector3 lookAtPos)
  541. {
  542. float maxDistance = targetDistance - (ray.origin - lookAtPos).magnitude;
  543. if (maxDistance < Epsilon)
  544. return 0;
  545. if (m_Strategy == ResolutionStrategy.PreserveCameraDistance)
  546. return maxDistance;
  547. float distance;
  548. if (!startPlane.Raycast(ray, out distance))
  549. distance = 0;
  550. distance = Mathf.Min(maxDistance, distance);
  551. if (distance < Epsilon)
  552. return 0;
  553. // If we are close to parallel to the plane, we have to take special action
  554. float angle = Mathf.Abs(UnityVectorExtensions.Angle(startPlane.normal, ray.direction) - 90);
  555. if (angle < AngleThreshold)
  556. distance = Mathf.Lerp(0, distance, angle / AngleThreshold);
  557. return distance;
  558. }
  559. float ClampRayToBounds(Ray ray, float distance, Bounds bounds)
  560. {
  561. float d;
  562. if (Vector3.Dot(ray.direction, Vector3.up) > 0)
  563. {
  564. if (new Plane(Vector3.down, bounds.max).Raycast(ray, out d) && d > Epsilon)
  565. distance = Mathf.Min(distance, d);
  566. }
  567. else if (Vector3.Dot(ray.direction, Vector3.down) > 0)
  568. {
  569. if (new Plane(Vector3.up, bounds.min).Raycast(ray, out d) && d > Epsilon)
  570. distance = Mathf.Min(distance, d);
  571. }
  572. if (Vector3.Dot(ray.direction, Vector3.right) > 0)
  573. {
  574. if (new Plane(Vector3.left, bounds.max).Raycast(ray, out d) && d > Epsilon)
  575. distance = Mathf.Min(distance, d);
  576. }
  577. else if (Vector3.Dot(ray.direction, Vector3.left) > 0)
  578. {
  579. if (new Plane(Vector3.right, bounds.min).Raycast(ray, out d) && d > Epsilon)
  580. distance = Mathf.Min(distance, d);
  581. }
  582. if (Vector3.Dot(ray.direction, Vector3.forward) > 0)
  583. {
  584. if (new Plane(Vector3.back, bounds.max).Raycast(ray, out d) && d > Epsilon)
  585. distance = Mathf.Min(distance, d);
  586. }
  587. else if (Vector3.Dot(ray.direction, Vector3.back) > 0)
  588. {
  589. if (new Plane(Vector3.forward, bounds.min).Raycast(ray, out d) && d > Epsilon)
  590. distance = Mathf.Min(distance, d);
  591. }
  592. return distance;
  593. }
  594. private Collider[] mColliderBuffer = new Collider[5];
  595. private static SphereCollider mCameraCollider;
  596. private static GameObject mCameraColliderGameObject;
  597. static void DestroyCollider()
  598. {
  599. if (mCameraColliderGameObject != null)
  600. {
  601. mCameraColliderGameObject.SetActive(false);
  602. RuntimeUtility.DestroyObject(mCameraColliderGameObject.GetComponent<Rigidbody>());
  603. }
  604. RuntimeUtility.DestroyObject(mCameraCollider);
  605. RuntimeUtility.DestroyObject(mCameraColliderGameObject);
  606. mCameraColliderGameObject = null;
  607. mCameraCollider = null;
  608. }
  609. private Vector3 RespectCameraRadius(Vector3 cameraPos, ref CameraState state)
  610. {
  611. Vector3 result = Vector3.zero;
  612. if (m_CameraRadius < Epsilon || m_CollideAgainst == 0)
  613. return result;
  614. Vector3 dir = state.HasLookAt ? (cameraPos - state.ReferenceLookAt) : Vector3.zero;
  615. Ray ray = new Ray();
  616. float distance = dir.magnitude;
  617. if (distance > Epsilon)
  618. {
  619. dir /= distance;
  620. ray = new Ray(state.ReferenceLookAt, dir);
  621. }
  622. // Pull it out of any intersecting obstacles
  623. RaycastHit hitInfo;
  624. int numObstacles = Physics.OverlapSphereNonAlloc(
  625. cameraPos, m_CameraRadius, mColliderBuffer,
  626. m_CollideAgainst, QueryTriggerInteraction.Ignore);
  627. if (numObstacles == 0 && m_TransparentLayers != 0
  628. && distance > m_MinimumDistanceFromTarget + Epsilon)
  629. {
  630. // Make sure the camera position isn't completely inside an obstacle.
  631. // OverlapSphereNonAlloc won't catch those.
  632. float d = distance - m_MinimumDistanceFromTarget;
  633. Vector3 targetPos = state.ReferenceLookAt + dir * m_MinimumDistanceFromTarget;
  634. if (RuntimeUtility.RaycastIgnoreTag(new Ray(targetPos, dir),
  635. out hitInfo, d, m_CollideAgainst, m_IgnoreTag))
  636. {
  637. // Only count it if there's an incoming collision but not an outgoing one
  638. Collider c = hitInfo.collider;
  639. if (!c.Raycast(new Ray(cameraPos, -dir), out hitInfo, d))
  640. mColliderBuffer[numObstacles++] = c;
  641. }
  642. }
  643. if (numObstacles > 0 && distance == 0 || distance > m_MinimumDistanceFromTarget)
  644. {
  645. if (mCameraColliderGameObject == null)
  646. {
  647. mCameraColliderGameObject = new GameObject("CinemachineCollider Collider");
  648. mCameraColliderGameObject.hideFlags = HideFlags.HideAndDontSave;
  649. mCameraColliderGameObject.transform.position = Vector3.zero;
  650. mCameraColliderGameObject.SetActive(true);
  651. mCameraCollider = mCameraColliderGameObject.AddComponent<SphereCollider>();
  652. mCameraCollider.isTrigger = true;
  653. var rb = mCameraColliderGameObject.AddComponent<Rigidbody>();
  654. rb.detectCollisions = false;
  655. rb.isKinematic = true;
  656. }
  657. mCameraCollider.radius = m_CameraRadius;
  658. Vector3 offsetDir;
  659. float offsetDistance;
  660. Vector3 newCamPos = cameraPos;
  661. for (int i = 0; i < numObstacles; ++i)
  662. {
  663. Collider c = mColliderBuffer[i];
  664. if (m_IgnoreTag.Length > 0 && c.CompareTag(m_IgnoreTag))
  665. continue;
  666. // If we have a lookAt target, move the camera to the nearest edge of obstacle
  667. if (distance > m_MinimumDistanceFromTarget)
  668. {
  669. dir = newCamPos - state.ReferenceLookAt;
  670. float d = dir.magnitude;
  671. if (d > Epsilon)
  672. {
  673. dir /= d;
  674. ray = new Ray(state.ReferenceLookAt, dir);
  675. if (c.Raycast(ray, out hitInfo, d + m_CameraRadius))
  676. newCamPos = ray.GetPoint(hitInfo.distance) - (dir * PrecisionSlush);
  677. }
  678. }
  679. if (Physics.ComputePenetration(
  680. mCameraCollider, newCamPos, Quaternion.identity,
  681. c, c.transform.position, c.transform.rotation,
  682. out offsetDir, out offsetDistance))
  683. {
  684. newCamPos += offsetDir * offsetDistance;
  685. }
  686. }
  687. result = newCamPos - cameraPos;
  688. }
  689. // Respect the minimum distance from target - push camera back if we have to
  690. if (distance > Epsilon)
  691. {
  692. float minDistance = Mathf.Max(m_MinimumDistanceFromTarget, m_CameraRadius) + PrecisionSlush;
  693. Vector3 newOffset = cameraPos + result - state.ReferenceLookAt;
  694. if (newOffset.magnitude < minDistance)
  695. result = state.ReferenceLookAt - cameraPos + dir * minDistance;
  696. }
  697. return result;
  698. }
  699. private bool CheckForTargetObstructions(CameraState state)
  700. {
  701. if (state.HasLookAt)
  702. {
  703. Vector3 lookAtPos = state.ReferenceLookAt;
  704. Vector3 pos = state.CorrectedPosition;
  705. Vector3 dir = lookAtPos - pos;
  706. float distance = dir.magnitude;
  707. if (distance < Mathf.Max(m_MinimumDistanceFromTarget, Epsilon))
  708. return true;
  709. Ray ray = new Ray(pos, dir.normalized);
  710. RaycastHit hitInfo;
  711. if (RuntimeUtility.RaycastIgnoreTag(ray, out hitInfo,
  712. distance - m_MinimumDistanceFromTarget,
  713. m_CollideAgainst & ~m_TransparentLayers, m_IgnoreTag))
  714. return true;
  715. }
  716. return false;
  717. }
  718. private bool IsTargetOffscreen(CameraState state)
  719. {
  720. if (state.HasLookAt)
  721. {
  722. Vector3 dir = state.ReferenceLookAt - state.CorrectedPosition;
  723. dir = Quaternion.Inverse(state.CorrectedOrientation) * dir;
  724. if (state.Lens.Orthographic)
  725. {
  726. if (Mathf.Abs(dir.y) > state.Lens.OrthographicSize)
  727. return true;
  728. if (Mathf.Abs(dir.x) > state.Lens.OrthographicSize * state.Lens.Aspect)
  729. return true;
  730. }
  731. else
  732. {
  733. float fov = state.Lens.FieldOfView / 2;
  734. float angle = UnityVectorExtensions.Angle(dir.ProjectOntoPlane(Vector3.right), Vector3.forward);
  735. if (angle > fov)
  736. return true;
  737. fov = Mathf.Rad2Deg * Mathf.Atan(Mathf.Tan(fov * Mathf.Deg2Rad) * state.Lens.Aspect);
  738. angle = UnityVectorExtensions.Angle(dir.ProjectOntoPlane(Vector3.up), Vector3.forward);
  739. if (angle > fov)
  740. return true;
  741. }
  742. }
  743. return false;
  744. }
  745. }
  746. #endif
  747. }