DynamicBoneCollider.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. using UnityEngine;
  2. [AddComponentMenu("Dynamic Bone/Dynamic Bone Collider")]
  3. public class DynamicBoneCollider : DynamicBoneColliderBase
  4. {
  5. #if UNITY_5_3_OR_NEWER
  6. [Tooltip("The radius of the sphere or capsule.")]
  7. #endif
  8. public float m_Radius = 0.5f;
  9. #if UNITY_5_3_OR_NEWER
  10. [Tooltip("The height of the capsule.")]
  11. #endif
  12. public float m_Height = 0;
  13. #if UNITY_5_3_OR_NEWER
  14. [Tooltip("The other radius of the capsule.")]
  15. #endif
  16. public float m_Radius2 = 0;
  17. // prepare data
  18. float m_ScaledRadius;
  19. float m_ScaledRadius2;
  20. Vector3 m_C0;
  21. Vector3 m_C1;
  22. float m_C01Distance;
  23. int m_CollideType;
  24. void OnValidate()
  25. {
  26. m_Radius = Mathf.Max(m_Radius, 0);
  27. m_Height = Mathf.Max(m_Height, 0);
  28. m_Radius2 = Mathf.Max(m_Radius2, 0);
  29. }
  30. public override void Prepare()
  31. {
  32. float scale = Mathf.Abs(transform.lossyScale.x);
  33. float halfHeight = m_Height * 0.5f;
  34. if (m_Radius2 <= 0 || Mathf.Abs(m_Radius - m_Radius2) < 0.01f)
  35. {
  36. m_ScaledRadius = m_Radius * scale;
  37. float h = halfHeight - m_Radius;
  38. if (h <= 0)
  39. {
  40. m_C0 = transform.TransformPoint(m_Center);
  41. if (m_Bound == Bound.Outside)
  42. {
  43. m_CollideType = 0;
  44. }
  45. else
  46. {
  47. m_CollideType = 1;
  48. }
  49. }
  50. else
  51. {
  52. Vector3 c0 = m_Center;
  53. Vector3 c1 = m_Center;
  54. switch (m_Direction)
  55. {
  56. case Direction.X:
  57. c0.x += h;
  58. c1.x -= h;
  59. break;
  60. case Direction.Y:
  61. c0.y += h;
  62. c1.y -= h;
  63. break;
  64. case Direction.Z:
  65. c0.z += h;
  66. c1.z -= h;
  67. break;
  68. }
  69. m_C0 = transform.TransformPoint(c0);
  70. m_C1 = transform.TransformPoint(c1);
  71. m_C01Distance = (m_C1 - m_C0).magnitude;
  72. if (m_Bound == Bound.Outside)
  73. {
  74. m_CollideType = 2;
  75. }
  76. else
  77. {
  78. m_CollideType = 3;
  79. }
  80. }
  81. }
  82. else
  83. {
  84. float r = Mathf.Max(m_Radius, m_Radius2);
  85. if (halfHeight - r <= 0)
  86. {
  87. m_ScaledRadius = r * scale;
  88. m_C0 = transform.TransformPoint(m_Center);
  89. if (m_Bound == Bound.Outside)
  90. {
  91. m_CollideType = 0;
  92. }
  93. else
  94. {
  95. m_CollideType = 1;
  96. }
  97. }
  98. else
  99. {
  100. m_ScaledRadius = m_Radius * scale;
  101. m_ScaledRadius2 = m_Radius2 * scale;
  102. float h0 = halfHeight - m_Radius;
  103. float h1 = halfHeight - m_Radius2;
  104. Vector3 c0 = m_Center;
  105. Vector3 c1 = m_Center;
  106. switch (m_Direction)
  107. {
  108. case Direction.X:
  109. c0.x += h0;
  110. c1.x -= h1;
  111. break;
  112. case Direction.Y:
  113. c0.y += h0;
  114. c1.y -= h1;
  115. break;
  116. case Direction.Z:
  117. c0.z += h0;
  118. c1.z -= h1;
  119. break;
  120. }
  121. m_C0 = transform.TransformPoint(c0);
  122. m_C1 = transform.TransformPoint(c1);
  123. m_C01Distance = (m_C1 - m_C0).magnitude;
  124. if (m_Bound == Bound.Outside)
  125. {
  126. m_CollideType = 4;
  127. }
  128. else
  129. {
  130. m_CollideType = 5;
  131. }
  132. }
  133. }
  134. }
  135. public override bool Collide(ref Vector3 particlePosition, float particleRadius)
  136. {
  137. switch (m_CollideType)
  138. {
  139. case 0:
  140. return OutsideSphere(ref particlePosition, particleRadius, m_C0, m_ScaledRadius);
  141. case 1:
  142. return InsideSphere(ref particlePosition, particleRadius, m_C0, m_ScaledRadius);
  143. case 2:
  144. return OutsideCapsule(ref particlePosition, particleRadius, m_C0, m_C1, m_ScaledRadius, m_C01Distance);
  145. case 3:
  146. return InsideCapsule(ref particlePosition, particleRadius, m_C0, m_C1, m_ScaledRadius, m_C01Distance);
  147. case 4:
  148. return OutsideCapsule2(ref particlePosition, particleRadius, m_C0, m_C1, m_ScaledRadius, m_ScaledRadius2, m_C01Distance);
  149. case 5:
  150. return InsideCapsule2(ref particlePosition, particleRadius, m_C0, m_C1, m_ScaledRadius, m_ScaledRadius2, m_C01Distance);
  151. }
  152. return false;
  153. }
  154. static bool OutsideSphere(ref Vector3 particlePosition, float particleRadius, Vector3 sphereCenter, float sphereRadius)
  155. {
  156. float r = sphereRadius + particleRadius;
  157. float r2 = r * r;
  158. Vector3 d = particlePosition - sphereCenter;
  159. float dlen2 = d.sqrMagnitude;
  160. // if is inside sphere, project onto sphere surface
  161. if (dlen2 > 0 && dlen2 < r2)
  162. {
  163. float dlen = Mathf.Sqrt(dlen2);
  164. particlePosition = sphereCenter + d * (r / dlen);
  165. return true;
  166. }
  167. return false;
  168. }
  169. static bool InsideSphere(ref Vector3 particlePosition, float particleRadius, Vector3 sphereCenter, float sphereRadius)
  170. {
  171. float r = sphereRadius - particleRadius;
  172. float r2 = r * r;
  173. Vector3 d = particlePosition - sphereCenter;
  174. float dlen2 = d.sqrMagnitude;
  175. // if is outside sphere, project onto sphere surface
  176. if (dlen2 > r2)
  177. {
  178. float dlen = Mathf.Sqrt(dlen2);
  179. particlePosition = sphereCenter + d * (r / dlen);
  180. return true;
  181. }
  182. return false;
  183. }
  184. static bool OutsideCapsule(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius, float dirlen)
  185. {
  186. float r = capsuleRadius + particleRadius;
  187. float r2 = r * r;
  188. Vector3 dir = capsuleP1 - capsuleP0;
  189. Vector3 d = particlePosition - capsuleP0;
  190. float t = Vector3.Dot(d, dir);
  191. if (t <= 0)
  192. {
  193. // check sphere1
  194. float dlen2 = d.sqrMagnitude;
  195. if (dlen2 > 0 && dlen2 < r2)
  196. {
  197. float dlen = Mathf.Sqrt(dlen2);
  198. particlePosition = capsuleP0 + d * (r / dlen);
  199. return true;
  200. }
  201. }
  202. else
  203. {
  204. float dirlen2 = dirlen * dirlen;
  205. if (t >= dirlen2)
  206. {
  207. // check sphere2
  208. d = particlePosition - capsuleP1;
  209. float dlen2 = d.sqrMagnitude;
  210. if (dlen2 > 0 && dlen2 < r2)
  211. {
  212. float dlen = Mathf.Sqrt(dlen2);
  213. particlePosition = capsuleP1 + d * (r / dlen);
  214. return true;
  215. }
  216. }
  217. else
  218. {
  219. // check cylinder
  220. Vector3 q = d - dir * (t / dirlen2);
  221. float qlen2 = q.sqrMagnitude;
  222. if (qlen2 > 0 && qlen2 < r2)
  223. {
  224. float qlen = Mathf.Sqrt(qlen2);
  225. particlePosition += q * ((r - qlen) / qlen);
  226. return true;
  227. }
  228. }
  229. }
  230. return false;
  231. }
  232. static bool InsideCapsule(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius, float dirlen)
  233. {
  234. float r = capsuleRadius - particleRadius;
  235. float r2 = r * r;
  236. Vector3 dir = capsuleP1 - capsuleP0;
  237. Vector3 d = particlePosition - capsuleP0;
  238. float t = Vector3.Dot(d, dir);
  239. if (t <= 0)
  240. {
  241. // check sphere1
  242. float dlen2 = d.sqrMagnitude;
  243. if (dlen2 > r2)
  244. {
  245. float dlen = Mathf.Sqrt(dlen2);
  246. particlePosition = capsuleP0 + d * (r / dlen);
  247. return true;
  248. }
  249. }
  250. else
  251. {
  252. float dirlen2 = dirlen * dirlen;
  253. if (t >= dirlen2)
  254. {
  255. // check sphere2
  256. d = particlePosition - capsuleP1;
  257. float dlen2 = d.sqrMagnitude;
  258. if (dlen2 > r2)
  259. {
  260. float dlen = Mathf.Sqrt(dlen2);
  261. particlePosition = capsuleP1 + d * (r / dlen);
  262. return true;
  263. }
  264. }
  265. else
  266. {
  267. // check cylinder
  268. Vector3 q = d - dir * (t / dirlen2);
  269. float qlen2 = q.sqrMagnitude;
  270. if (qlen2 > r2)
  271. {
  272. float qlen = Mathf.Sqrt(qlen2);
  273. particlePosition += q * ((r - qlen) / qlen);
  274. return true;
  275. }
  276. }
  277. }
  278. return false;
  279. }
  280. static bool OutsideCapsule2(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius0, float capsuleRadius1, float dirlen)
  281. {
  282. Vector3 dir = capsuleP1 - capsuleP0;
  283. Vector3 d = particlePosition - capsuleP0;
  284. float t = Vector3.Dot(d, dir);
  285. if (t <= 0)
  286. {
  287. // check sphere1
  288. float r = capsuleRadius0 + particleRadius;
  289. float r2 = r * r;
  290. float dlen2 = d.sqrMagnitude;
  291. if (dlen2 > 0 && dlen2 < r2)
  292. {
  293. float dlen = Mathf.Sqrt(dlen2);
  294. particlePosition = capsuleP0 + d * (r / dlen);
  295. return true;
  296. }
  297. }
  298. else
  299. {
  300. float dirlen2 = dirlen * dirlen;
  301. if (t >= dirlen2)
  302. {
  303. // check sphere2
  304. float r = capsuleRadius1 + particleRadius;
  305. float r2 = r * r;
  306. d = particlePosition - capsuleP1;
  307. float dlen2 = d.sqrMagnitude;
  308. if (dlen2 > 0 && dlen2 < r2)
  309. {
  310. float dlen = Mathf.Sqrt(dlen2);
  311. particlePosition = capsuleP1 + d * (r / dlen);
  312. return true;
  313. }
  314. }
  315. else
  316. {
  317. // check cylinder
  318. Vector3 q = d - dir * (t / dirlen2);
  319. float qlen2 = q.sqrMagnitude;
  320. float klen = Vector3.Dot(d, dir / dirlen);
  321. float r = Mathf.Lerp(capsuleRadius0, capsuleRadius1, klen / dirlen) + particleRadius;
  322. float r2 = r * r;
  323. if (qlen2 > 0 && qlen2 < r2)
  324. {
  325. float qlen = Mathf.Sqrt(qlen2);
  326. particlePosition += q * ((r - qlen) / qlen);
  327. return true;
  328. }
  329. }
  330. }
  331. return false;
  332. }
  333. static bool InsideCapsule2(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius0, float capsuleRadius1, float dirlen)
  334. {
  335. Vector3 dir = capsuleP1 - capsuleP0;
  336. Vector3 d = particlePosition - capsuleP0;
  337. float t = Vector3.Dot(d, dir);
  338. if (t <= 0)
  339. {
  340. // check sphere1
  341. float r = capsuleRadius0 - particleRadius;
  342. float r2 = r * r;
  343. float dlen2 = d.sqrMagnitude;
  344. if (dlen2 > r2)
  345. {
  346. float dlen = Mathf.Sqrt(dlen2);
  347. particlePosition = capsuleP0 + d * (r / dlen);
  348. return true;
  349. }
  350. }
  351. else
  352. {
  353. float dirlen2 = dirlen * dirlen;
  354. if (t >= dirlen2)
  355. {
  356. // check sphere2
  357. float r = capsuleRadius1 - particleRadius;
  358. float r2 = r * r;
  359. d = particlePosition - capsuleP1;
  360. float dlen2 = d.sqrMagnitude;
  361. if (dlen2 > r2)
  362. {
  363. float dlen = Mathf.Sqrt(dlen2);
  364. particlePosition = capsuleP1 + d * (r / dlen);
  365. return true;
  366. }
  367. }
  368. else
  369. {
  370. // check cylinder
  371. Vector3 q = d - dir * (t / dirlen2);
  372. float qlen2 = q.sqrMagnitude;
  373. float klen = Vector3.Dot(d, dir / dirlen);
  374. float r = Mathf.Lerp(capsuleRadius0, capsuleRadius1, klen / dirlen) - particleRadius;
  375. float r2 = r * r;
  376. if (qlen2 > r2)
  377. {
  378. float qlen = Mathf.Sqrt(qlen2);
  379. particlePosition += q * ((r - qlen) / qlen);
  380. return true;
  381. }
  382. }
  383. }
  384. return false;
  385. }
  386. void OnDrawGizmosSelected()
  387. {
  388. if (!enabled)
  389. return;
  390. Prepare();
  391. if (m_Bound == Bound.Outside)
  392. {
  393. Gizmos.color = Color.yellow;
  394. }
  395. else
  396. {
  397. Gizmos.color = Color.magenta;
  398. }
  399. switch (m_CollideType)
  400. {
  401. case 0:
  402. case 1:
  403. Gizmos.DrawWireSphere(m_C0, m_ScaledRadius);
  404. break;
  405. case 2:
  406. case 3:
  407. DrawCapsule(m_C0, m_C1, m_ScaledRadius, m_ScaledRadius);
  408. break;
  409. case 4:
  410. case 5:
  411. DrawCapsule(m_C0, m_C1, m_ScaledRadius, m_ScaledRadius2);
  412. break;
  413. }
  414. }
  415. static void DrawCapsule(Vector3 c0, Vector3 c1, float radius0, float radius1)
  416. {
  417. Gizmos.DrawLine(c0, c1);
  418. Gizmos.DrawWireSphere(c0, radius0);
  419. Gizmos.DrawWireSphere(c1, radius1);
  420. }
  421. }