Collider.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System;
  4. namespace BansheeEngine
  5. {
  6. /** @addtogroup Physics
  7. * @{
  8. */
  9. /// <summary>
  10. /// Collider represents physics geometry that can be in multiple states:
  11. /// - Default: Static geometry that physics objects can collide with.
  12. /// - Trigger: Static geometry that can't be collided with but will report touch events.
  13. /// - Dynamic: Dynamic geometry that is a part of a Rigidbody.A set of colliders defines the shape of the parent
  14. /// rigidbody.
  15. /// </summary>
  16. public abstract class Collider : Component
  17. {
  18. internal NativeCollider native;
  19. protected Rigidbody parent;
  20. [SerializeField]
  21. internal SerializableData serializableData = new SerializableData();
  22. /// <summary>
  23. /// Triggered when some object starts interacting with the collider. Only triggered if proper collision report mode
  24. /// is turned on.
  25. /// </summary>
  26. public event Action<CollisionData> OnCollisionBegin;
  27. /// <summary>
  28. /// Triggered for every frame that an object remains interacting with a collider. Only triggered if proper collision
  29. /// report mode is turned on.
  30. /// </summary>
  31. public event Action<CollisionData> OnCollisionStay;
  32. /// <summary>
  33. /// Triggered when some object stops interacting with the collider. Only triggered if proper collision report mode
  34. /// is turned on.
  35. /// </summary>
  36. public event Action<CollisionData> OnCollisionEnd;
  37. /// <summary>
  38. /// Determines how the collider used. A trigger will not be used for collisions (objects will pass through it),
  39. /// but collision events will still be reported.
  40. /// </summary>
  41. public bool IsTrigger
  42. {
  43. get { return serializableData.isTrigger; }
  44. set
  45. {
  46. if (serializableData.isTrigger == value)
  47. return;
  48. serializableData.isTrigger = value;
  49. if (native != null)
  50. {
  51. native.IsTrigger = value;
  52. UpdateParentRigidbody();
  53. UpdateTransform();
  54. }
  55. }
  56. }
  57. /// <summary>
  58. /// Determines mass of the collider. Only relevant if the collider is part of a rigidbody. Ultimately this will
  59. /// determine the total mass, center of mass and inertia tensors of the parent rigidbody(if they're being calculated
  60. /// automatically).
  61. /// </summary>
  62. public float Mass
  63. {
  64. get { return serializableData.mass; }
  65. set
  66. {
  67. if (serializableData.mass == value)
  68. return;
  69. serializableData.mass = value;
  70. if (native != null)
  71. {
  72. native.Mass = value;
  73. if (parent != null)
  74. parent.UpdateMassDistribution();
  75. }
  76. }
  77. }
  78. /// <summary>
  79. /// Physics material that determines how objects hitting the collider behave.
  80. /// </summary>
  81. public PhysicsMaterial Material
  82. {
  83. get { return serializableData.material; }
  84. set
  85. {
  86. serializableData.material = value;
  87. if (native != null)
  88. native.Material = value;
  89. }
  90. }
  91. /// <summary>
  92. /// Determines how far apart do two shapes need to be away from each other before the physics runtime starts
  93. /// generating repelling impulse for them.This distance will be the sum of contact offsets of the two interacting
  94. /// objects.If objects are moving fast you can increase this value to start generating the impulse earlier and
  95. /// potentially prevent the objects from interpenetrating. This value is in meters. Must be positive and larger
  96. /// than <see cref="RestOffset"/>.
  97. /// </summary>
  98. public float ContactOffset
  99. {
  100. get { return serializableData.contactOffset; }
  101. set
  102. {
  103. value = MathEx.Max(0, MathEx.Max(value, RestOffset));
  104. serializableData.contactOffset = value;
  105. if (native != null)
  106. native.ContactOffset = value;
  107. }
  108. }
  109. /// <summary>
  110. /// Determines at what distance should two objects resting on one another come to an equilibrium. The value used in the
  111. /// runtime will be the sum of rest offsets for both interacting objects. This value is in meters. Cannot be larger
  112. /// than <see cref="ContactOffset"/>
  113. /// </summary>
  114. public float RestOffset
  115. {
  116. get { return serializableData.restOffset; }
  117. set
  118. {
  119. value = MathEx.Min(value, ContactOffset);
  120. serializableData.restOffset = value;
  121. if (native != null)
  122. native.RestOffset = value;
  123. }
  124. }
  125. /// <summary>
  126. /// Determines with which objects will the collider collide. Objects that are allowed to collide with this
  127. /// object must have the same bits set in their own layers.
  128. /// </summary>
  129. public ulong Layer
  130. {
  131. get { return serializableData.layer; }
  132. set
  133. {
  134. serializableData.layer = value;
  135. if (native != null)
  136. native.Layer = value;
  137. }
  138. }
  139. /// <summary>
  140. /// Determines which (if any) collision events are reported.
  141. /// </summary>
  142. public CollisionReportMode CollisionReportMode
  143. {
  144. get { return serializableData.collisionReportMode; }
  145. set
  146. {
  147. serializableData.collisionReportMode = value;
  148. if (native != null)
  149. UpdateCollisionReportMode();
  150. }
  151. }
  152. /// <summary>
  153. /// Checks does the ray hit this collider.
  154. /// </summary>
  155. /// <param name="ray">Ray to check.</param>
  156. /// <param name="hit">Information about the hit. Valid only if the method returns true.</param>
  157. /// <param name="maxDist">Maximum distance from the ray origin to search for hits.</param>
  158. /// <returns>True if the ray has hit the collider.</returns>
  159. public bool Raycast(Ray ray, out PhysicsQueryHit hit, float maxDist)
  160. {
  161. return Raycast(ray.origin, ray.direction, out hit, maxDist);
  162. }
  163. /// <summary>
  164. /// Checks does the ray hit this collider.
  165. /// </summary>
  166. /// <param name="origin">Origin of the ray to check.</param>
  167. /// <param name="unitDir">Unit direction of the ray to check.</param>
  168. /// <param name="hit">Information about the hit. Valid only if the method returns true.</param>
  169. /// <param name="maxDist">Maximum distance from the ray origin to search for hits.</param>
  170. /// <returns>True if the ray has hit the collider.</returns>
  171. public bool Raycast(Vector3 origin, Vector3 unitDir, out PhysicsQueryHit hit, float maxDist = float.MaxValue)
  172. {
  173. hit = new PhysicsQueryHit();
  174. if (native == null)
  175. return false;
  176. ScriptPhysicsQueryHit scriptHit;
  177. bool wasHit = native.Raycast(origin, unitDir, out scriptHit, maxDist);
  178. hit.collider = scriptHit.collider.Component;
  179. hit.distance = scriptHit.distance;
  180. hit.normal = scriptHit.normal;
  181. hit.point = scriptHit.point;
  182. hit.triangleIdx = scriptHit.triangleIdx;
  183. hit.uv = scriptHit.uv;
  184. return wasHit;
  185. }
  186. /// <summary>
  187. /// Creates an internal instance of the collider. Should be overriden by specific collider implementations.
  188. /// </summary>
  189. /// <returns>An instance of a specific internal collider implementation. </returns>
  190. internal abstract NativeCollider CreateCollider();
  191. /// <summary>
  192. /// Changes the rigidbody parent of the collider. Meant to be called from the Rigidbody itself.
  193. /// </summary>
  194. /// <param name="rigidbody">New rigidbody to assign as the parent to the collider.</param>
  195. /// <param name="isInternal">If true the rigidbody will just be changed internally, but parent rigidbody will not be
  196. /// notified.</param>
  197. internal void SetRigidbody(Rigidbody rigidbody, bool isInternal = false)
  198. {
  199. if (rigidbody == parent)
  200. return;
  201. if (native != null && !isInternal)
  202. {
  203. if (parent != null)
  204. parent.RemoveCollider(this);
  205. NativeRigidbody nativeRigidbody = null;
  206. if (rigidbody != null)
  207. nativeRigidbody = rigidbody.native;
  208. native.Rigidbody = nativeRigidbody;
  209. if (rigidbody != null)
  210. rigidbody.AddCollider(this);
  211. }
  212. parent = rigidbody;
  213. UpdateCollisionReportMode();
  214. }
  215. /// <summary>
  216. /// Triggered when the internal collider begins touching another object.
  217. /// </summary>
  218. /// <param name="data">Data about the collision.</param>
  219. internal void DoOnCollisionBegin(CollisionData data)
  220. {
  221. if (OnCollisionBegin != null)
  222. OnCollisionBegin(data);
  223. }
  224. /// <summary>
  225. /// Triggered when the internal collider ends touching another object.
  226. /// </summary>
  227. /// <param name="data">Data about the collision.</param>
  228. internal void DoOnCollisionStay(CollisionData data)
  229. {
  230. if (OnCollisionStay != null)
  231. OnCollisionStay(data);
  232. }
  233. /// <summary>
  234. /// Triggered when the internal collider ends touching another object.
  235. /// </summary>
  236. /// <param name="data">Data about the collision.</param>
  237. internal void DoOnCollisionEnd(CollisionData data)
  238. {
  239. if (OnCollisionEnd != null)
  240. OnCollisionEnd(data);
  241. }
  242. /// <summary>
  243. /// Checks is the provided rigidbody a valid parent for this collider.
  244. ///
  245. /// This is required because certain colliders are limited in how they can be used.
  246. /// </summary>
  247. /// <param name="parent">Rigidbody that is the potential parent.</param>
  248. /// <returns>True if collider can be a part of the rigidbody.</returns>
  249. protected internal virtual bool IsValidParent(Rigidbody parent)
  250. {
  251. return true;
  252. }
  253. /// <summary>
  254. /// Searches the parent scene object hierarchy to find a parent Rigidbody component.
  255. /// </summary>
  256. protected void UpdateParentRigidbody()
  257. {
  258. if (serializableData.isTrigger)
  259. {
  260. SetRigidbody(null);
  261. return;
  262. }
  263. SceneObject currentSO = SceneObject;
  264. while (currentSO != null)
  265. {
  266. Rigidbody parent = currentSO.GetComponent<Rigidbody>();
  267. if (parent != null)
  268. {
  269. if (currentSO.Active && IsValidParent(parent))
  270. SetRigidbody(parent);
  271. else
  272. SetRigidbody(null);
  273. return;
  274. }
  275. currentSO = currentSO.Parent;
  276. }
  277. // Not found
  278. SetRigidbody(null);
  279. }
  280. /// <summary>
  281. /// Updates the transform of the internal Collider representation from the transform of the component's scene object.
  282. /// </summary>
  283. protected void UpdateTransform()
  284. {
  285. Vector3 myScale = SceneObject.Scale;
  286. if (parent != null)
  287. {
  288. Vector3 parentPos = parent.SceneObject.Position;
  289. Quaternion parentRot = parent.SceneObject.Rotation;
  290. Vector3 myPos = SceneObject.Position;
  291. Quaternion myRot = SceneObject.Rotation;
  292. Vector3 scale = parent.SceneObject.Scale;
  293. Vector3 invScale = scale;
  294. if (invScale.x != 0) invScale.x = 1.0f / invScale.x;
  295. if (invScale.y != 0) invScale.y = 1.0f / invScale.y;
  296. if (invScale.z != 0) invScale.z = 1.0f / invScale.z;
  297. Quaternion invRotation = parentRot.Inverse;
  298. Vector3 relativePos = invRotation.Rotate(myPos - parentPos) * invScale;
  299. Quaternion relativeRot = invRotation * myRot;
  300. relativePos = relativePos + myRot.Rotate(serializableData.localPosition * scale);
  301. relativeRot = relativeRot * serializableData.localRotation;
  302. native.Position = relativePos;
  303. native.Rotation = relativeRot;
  304. parent.UpdateMassDistribution();
  305. }
  306. else
  307. {
  308. Quaternion myRot = SceneObject.Rotation;
  309. Vector3 myPos = SceneObject.Position + myRot.Rotate(serializableData.localPosition * myScale);
  310. myRot = myRot * serializableData.localRotation;
  311. native.Position = myPos;
  312. native.Rotation = myRot;
  313. }
  314. native.Scale = myScale;
  315. }
  316. /// <summary>
  317. /// Applies the collision report mode to the internal collider depending on the current state.
  318. /// </summary>
  319. internal void UpdateCollisionReportMode()
  320. {
  321. CollisionReportMode mode = serializableData.collisionReportMode;
  322. if (parent != null)
  323. mode = parent.CollisionReportMode;
  324. if(native != null)
  325. native.CollisionReportMode = mode;
  326. }
  327. private void OnInitialize()
  328. {
  329. NotifyFlags = TransformChangedFlags.Transform | TransformChangedFlags.Parent;
  330. }
  331. private void OnEnable()
  332. {
  333. RestoreNative();
  334. }
  335. private void OnDisable()
  336. {
  337. DestroyNative();
  338. }
  339. private void OnDestroy()
  340. {
  341. DestroyNative();
  342. }
  343. private void OnTransformChanged(TransformChangedFlags flags)
  344. {
  345. if (!SceneObject.Active)
  346. return;
  347. if ((flags & TransformChangedFlags.Parent) != 0)
  348. UpdateParentRigidbody();
  349. // Don't update the transform if it's due to Physics update since then we can guarantee it will remain at the same
  350. // relative transform to its parent
  351. if (Physics.IsUpdateInProgress)
  352. return;
  353. if ((flags & (TransformChangedFlags.Parent | TransformChangedFlags.Transform)) != 0)
  354. UpdateTransform();
  355. }
  356. /// <summary>
  357. /// Destroys the internal collider representation.
  358. /// </summary>
  359. private void DestroyNative()
  360. {
  361. if (parent != null)
  362. parent.RemoveCollider(this);
  363. parent = null;
  364. if (native != null)
  365. {
  366. native.Destroy();
  367. native = null;
  368. }
  369. }
  370. /// <summary>
  371. /// Creates the internal representation of the Collider and restores the values saved by the Component.
  372. /// </summary>
  373. private void RestoreNative()
  374. {
  375. native = CreateCollider();
  376. native.Component = this;
  377. native.Position = serializableData.localPosition;
  378. native.Rotation = serializableData.localRotation;
  379. native.IsTrigger = serializableData.isTrigger;
  380. native.Mass = serializableData.mass;
  381. native.Material = serializableData.material;
  382. native.ContactOffset = serializableData.contactOffset;
  383. native.RestOffset = serializableData.restOffset;
  384. native.Layer = serializableData.layer;
  385. UpdateParentRigidbody();
  386. UpdateTransform();
  387. UpdateCollisionReportMode();
  388. }
  389. /// <summary>
  390. /// Holds all data the collider component needs to persist through serialization.
  391. /// </summary>
  392. [SerializeObject]
  393. internal class SerializableData
  394. {
  395. public Vector3 localPosition = Vector3.Zero;
  396. public Quaternion localRotation = Quaternion.Identity;
  397. public bool isTrigger = false;
  398. public float mass = 1.0f;
  399. public PhysicsMaterial material;
  400. public float contactOffset = 0.02f;
  401. public float restOffset = 0.0f;
  402. public ulong layer = 1;
  403. public CollisionReportMode collisionReportMode = CollisionReportMode.None;
  404. }
  405. }
  406. /** @} */
  407. }