InteractionSystem.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine.Serialization;
  5. namespace RootMotion.FinalIK {
  6. /// <summary>
  7. /// Handles FBBIK interactions for a character.
  8. /// </summary>
  9. [HelpURL("https://www.youtube.com/watch?v=r5jiZnsDH3M")]
  10. [AddComponentMenu("Scripts/RootMotion.FinalIK/Interaction System/Interaction System")]
  11. public class InteractionSystem : MonoBehaviour {
  12. // Open the User Manual URL
  13. [ContextMenu("User Manual")]
  14. void OpenUserManual()
  15. {
  16. Application.OpenURL("http://www.root-motion.com/finalikdox/html/page10.html");
  17. }
  18. // Open the Script Reference URL
  19. [ContextMenu("Scrpt Reference")]
  20. void OpenScriptReference()
  21. {
  22. Application.OpenURL("http://www.root-motion.com/finalikdox/html/class_root_motion_1_1_final_i_k_1_1_interaction_system.html");
  23. }
  24. // Open a video tutorial video
  25. [ContextMenu("TUTORIAL VIDEO (PART 1: BASICS)")]
  26. void OpenTutorial1() {
  27. Application.OpenURL("https://www.youtube.com/watch?v=r5jiZnsDH3M");
  28. }
  29. // Open a video tutorial video
  30. [ContextMenu("TUTORIAL VIDEO (PART 2: PICKING UP...)")]
  31. void OpenTutorial2() {
  32. Application.OpenURL("https://www.youtube.com/watch?v=eP9-zycoHLk");
  33. }
  34. // Open a video tutorial video
  35. [ContextMenu("TUTORIAL VIDEO (PART 3: ANIMATION)")]
  36. void OpenTutorial3() {
  37. Application.OpenURL("https://www.youtube.com/watch?v=sQfB2RcT1T4&index=14&list=PLVxSIA1OaTOu8Nos3CalXbJ2DrKnntMv6");
  38. }
  39. // Open a video tutorial video
  40. [ContextMenu("TUTORIAL VIDEO (PART 4: TRIGGERS)")]
  41. void OpenTutorial4() {
  42. Application.OpenURL("https://www.youtube.com/watch?v=-TDZpNjt2mk&index=15&list=PLVxSIA1OaTOu8Nos3CalXbJ2DrKnntMv6");
  43. }
  44. // Link to the Final IK Google Group
  45. [ContextMenu("Support")]
  46. void SupportGroup() {
  47. Application.OpenURL("https://groups.google.com/forum/#!forum/final-ik");
  48. }
  49. // Link to the Final IK Asset Store thread in the Unity Community
  50. [ContextMenu("Asset Store Thread")]
  51. void ASThread() {
  52. Application.OpenURL("http://forum.unity3d.com/threads/final-ik-full-body-ik-aim-look-at-fabrik-ccd-ik-1-0-released.222685/");
  53. }
  54. #region Main Interface
  55. /// <summary>
  56. /// If not empty, only the targets with the specified tag will be used by this Interaction System.
  57. /// </summary>
  58. [Tooltip("If not empty, only the targets with the specified tag will be used by this Interaction System.")]
  59. public string targetTag = "";
  60. /// <summary>
  61. /// The fade in time of the interaction.
  62. /// </summary>
  63. [Tooltip("The fade in time of the interaction.")]
  64. public float fadeInTime = 0.3f;
  65. /// <summary>
  66. /// The master speed for all interactions.
  67. /// </summary>
  68. [Tooltip("The master speed for all interactions.")]
  69. public float speed = 1f;
  70. /// <summary>
  71. /// If > 0, lerps all the FBBIK channels used by the Interaction System back to their default or initial values when not in interaction.
  72. /// </summary>
  73. [Tooltip("If > 0, lerps all the FBBIK channels used by the Interaction System back to their default or initial values when not in interaction.")]
  74. public float resetToDefaultsSpeed = 1f;
  75. [Header("Triggering")]
  76. /// <summary>
  77. /// The collider that registers OnTriggerEnter and OnTriggerExit events with InteractionTriggers.
  78. /// </summary>
  79. [Tooltip("The collider that registers OnTriggerEnter and OnTriggerExit events with InteractionTriggers.")]
  80. [FormerlySerializedAs("collider")]
  81. public Collider characterCollider;
  82. /// <summary>
  83. /// Will be used by Interaction Triggers that need the camera's position. Assign the first person view character camera.
  84. /// </summary>
  85. [Tooltip("Will be used by Interaction Triggers that need the camera's position. Assign the first person view character camera.")]
  86. [FormerlySerializedAs("camera")]
  87. public Transform FPSCamera;
  88. /// <summary>
  89. /// The layers that will be raycasted from the camera (along camera.forward). All InteractionTrigger look at target colliders should be included.
  90. /// </summary>
  91. [Tooltip("The layers that will be raycasted from the camera (along camera.forward). All InteractionTrigger look at target colliders should be included.")]
  92. public LayerMask camRaycastLayers;
  93. /// <summary>
  94. /// Max distance of raycasting from the camera.
  95. /// </summary>
  96. [Tooltip("Max distance of raycasting from the camera.")]
  97. public float camRaycastDistance = 1f;
  98. /// <summary>
  99. /// Returns true if any of the effectors is in interaction and not paused.
  100. /// </summary>
  101. public bool inInteraction {
  102. get {
  103. if (!IsValid(true)) return false;
  104. for (int i = 0; i < interactionEffectors.Length; i++) {
  105. if (interactionEffectors[i].inInteraction && !interactionEffectors[i].isPaused) return true;
  106. }
  107. return false;
  108. }
  109. }
  110. /// <summary>
  111. /// Determines whether this effector is interaction and not paused
  112. /// </summary>
  113. public bool IsInInteraction(FullBodyBipedEffector effectorType) {
  114. if (!IsValid(true)) return false;
  115. for (int i = 0; i < interactionEffectors.Length; i++) {
  116. if (interactionEffectors[i].effectorType == effectorType) {
  117. return interactionEffectors[i].inInteraction && !interactionEffectors[i].isPaused;
  118. }
  119. }
  120. return false;
  121. }
  122. /// <summary>
  123. /// Determines whether this effector is paused
  124. /// </summary>
  125. public bool IsPaused(FullBodyBipedEffector effectorType) {
  126. if (!IsValid(true)) return false;
  127. for (int i = 0; i < interactionEffectors.Length; i++) {
  128. if (interactionEffectors[i].effectorType == effectorType) {
  129. return interactionEffectors[i].inInteraction && interactionEffectors[i].isPaused;
  130. }
  131. }
  132. return false;
  133. }
  134. /// <summary>
  135. /// Returns true if any of the effectors is paused
  136. /// </summary>
  137. public bool IsPaused() {
  138. if (!IsValid(true)) return false;
  139. for (int i = 0; i < interactionEffectors.Length; i++) {
  140. if (interactionEffectors[i].inInteraction && interactionEffectors[i].isPaused) return true;
  141. }
  142. return false;
  143. }
  144. /// <summary>
  145. /// Returns true if either all effectors in interaction are paused or none is.
  146. /// </summary>
  147. public bool IsInSync() {
  148. if (!IsValid(true)) return false;
  149. for (int i = 0; i < interactionEffectors.Length; i++) {
  150. if (interactionEffectors[i].isPaused) {
  151. for (int n = 0; n < interactionEffectors.Length; n++) {
  152. if (n != i && interactionEffectors[n].inInteraction && !interactionEffectors[n].isPaused) return false;
  153. }
  154. }
  155. }
  156. return true;
  157. }
  158. /// <summary>
  159. /// Starts the interaction between an effector and an interaction object.
  160. /// </summary>
  161. public bool StartInteraction(FullBodyBipedEffector effectorType, InteractionObject interactionObject, bool interrupt) {
  162. if (!IsValid(true)) return false;
  163. if (interactionObject == null) return false;
  164. for (int i = 0; i < interactionEffectors.Length; i++) {
  165. if (interactionEffectors[i].effectorType == effectorType) {
  166. return interactionEffectors[i].Start(interactionObject, targetTag, fadeInTime, interrupt);
  167. }
  168. }
  169. return false;
  170. }
  171. /// <summary>
  172. /// Pauses the interaction of an effector.
  173. /// </summary>
  174. public bool PauseInteraction(FullBodyBipedEffector effectorType) {
  175. if (!IsValid(true)) return false;
  176. for (int i = 0; i < interactionEffectors.Length; i++) {
  177. if (interactionEffectors[i].effectorType == effectorType) {
  178. return interactionEffectors[i].Pause();
  179. }
  180. }
  181. return false;
  182. }
  183. /// <summary>
  184. /// Resumes the paused interaction of an effector.
  185. /// </summary>
  186. public bool ResumeInteraction(FullBodyBipedEffector effectorType) {
  187. if (!IsValid(true)) return false;
  188. for (int i = 0; i < interactionEffectors.Length; i++) {
  189. if (interactionEffectors[i].effectorType == effectorType) {
  190. return interactionEffectors[i].Resume();
  191. }
  192. }
  193. return false;
  194. }
  195. /// <summary>
  196. /// Stops the interaction of an effector.
  197. /// </summary>
  198. public bool StopInteraction(FullBodyBipedEffector effectorType) {
  199. if (!IsValid(true)) return false;
  200. for (int i = 0; i < interactionEffectors.Length; i++) {
  201. if (interactionEffectors[i].effectorType == effectorType) {
  202. return interactionEffectors[i].Stop();
  203. }
  204. }
  205. return false;
  206. }
  207. /// <summary>
  208. /// Pauses all the interaction effectors.
  209. /// </summary>
  210. public void PauseAll() {
  211. if (!IsValid(true)) return;
  212. for (int i = 0; i < interactionEffectors.Length; i++) interactionEffectors[i].Pause();
  213. }
  214. /// <summary>
  215. /// Resumes all the paused interaction effectors.
  216. /// </summary>
  217. public void ResumeAll() {
  218. if (!IsValid(true)) return;
  219. for (int i = 0; i < interactionEffectors.Length; i++) interactionEffectors[i].Resume();
  220. }
  221. /// <summary>
  222. /// Stops all interactions.
  223. /// </summary>
  224. public void StopAll() {
  225. for (int i = 0; i < interactionEffectors.Length; i++) interactionEffectors[i].Stop();
  226. }
  227. /// <summary>
  228. /// Gets the current interaction object of an effector.
  229. /// </summary>
  230. public InteractionObject GetInteractionObject(FullBodyBipedEffector effectorType) {
  231. if (!IsValid(true)) return null;
  232. for (int i = 0; i < interactionEffectors.Length; i++) {
  233. if (interactionEffectors[i].effectorType == effectorType) {
  234. return interactionEffectors[i].interactionObject;
  235. }
  236. }
  237. return null;
  238. }
  239. /// <summary>
  240. /// Gets the progress of any interaction with the specified effector.
  241. /// </summary>
  242. public float GetProgress(FullBodyBipedEffector effectorType) {
  243. if (!IsValid(true)) return 0f;
  244. for (int i = 0; i < interactionEffectors.Length; i++) {
  245. if (interactionEffectors[i].effectorType == effectorType) {
  246. return interactionEffectors[i].progress;
  247. }
  248. }
  249. return 0f;
  250. }
  251. /// <summary>
  252. /// Gets the minimum progress of any active interaction
  253. /// </summary>
  254. public float GetMinActiveProgress() {
  255. if (!IsValid(true)) return 0f;
  256. float min = 1f;
  257. for (int i = 0; i < interactionEffectors.Length; i++) {
  258. if (interactionEffectors[i].inInteraction) {
  259. float p = interactionEffectors[i].progress;
  260. if (p > 0f && p < min) min = p;
  261. }
  262. }
  263. return min;
  264. }
  265. /// <summary>
  266. /// Triggers all interactions of an InteractionTrigger. Returns false if unsuccessful (maybe out of range).
  267. /// </summary>
  268. public bool TriggerInteraction(int index, bool interrupt) {
  269. if (!IsValid(true)) return false;
  270. if (!TriggerIndexIsValid(index)) return false;
  271. bool all = true;
  272. var range = triggersInRange[index].ranges[bestRangeIndexes[index]];
  273. for (int i = 0; i < range.interactions.Length; i++) {
  274. for (int e = 0; e < range.interactions[i].effectors.Length; e++) {
  275. bool s = StartInteraction(range.interactions[i].effectors[e], range.interactions[i].interactionObject, interrupt);
  276. if (!s) all = false;
  277. }
  278. }
  279. return all;
  280. }
  281. /// <summary>
  282. /// Triggers all interactions of an InteractionTrigger. Returns false if unsuccessful (maybe out of range).
  283. /// </summary>
  284. public bool TriggerInteraction(int index, bool interrupt, out InteractionObject interactionObject) {
  285. interactionObject = null;
  286. if (!IsValid(true)) return false;
  287. if (!TriggerIndexIsValid(index)) return false;
  288. bool all = true;
  289. var range = triggersInRange[index].ranges[bestRangeIndexes[index]];
  290. for (int i = 0; i < range.interactions.Length; i++) {
  291. for (int e = 0; e < range.interactions[i].effectors.Length; e++) {
  292. interactionObject = range.interactions[i].interactionObject;
  293. bool s = StartInteraction(range.interactions[i].effectors[e], interactionObject, interrupt);
  294. if (!s) all = false;
  295. }
  296. }
  297. return all;
  298. }
  299. /// <summary>
  300. /// Triggers all interactions of an InteractionTrigger. Returns false if unsuccessful (maybe out of range).
  301. /// </summary>
  302. public bool TriggerInteraction(int index, bool interrupt, out InteractionTarget interactionTarget) {
  303. interactionTarget = null;
  304. if (!IsValid(true)) return false;
  305. if (!TriggerIndexIsValid(index)) return false;
  306. bool all = true;
  307. var range = triggersInRange[index].ranges[bestRangeIndexes[index]];
  308. for (int i = 0; i < range.interactions.Length; i++) {
  309. for (int e = 0; e < range.interactions[i].effectors.Length; e++) {
  310. var interactionObject = range.interactions[i].interactionObject;
  311. var t = interactionObject.GetTarget(range.interactions[i].effectors[e], tag);
  312. if (t != null) interactionTarget = t.GetComponent<InteractionTarget>();
  313. bool s = StartInteraction(range.interactions[i].effectors[e], interactionObject, interrupt);
  314. if (!s) all = false;
  315. }
  316. }
  317. return all;
  318. }
  319. /// <summary>
  320. /// Gets the closest InteractionTrigger Range.
  321. /// </summary>
  322. public InteractionTrigger.Range GetClosestInteractionRange() {
  323. if (!IsValid(true)) return null;
  324. int index = GetClosestTriggerIndex();
  325. if (index < 0 || index >= triggersInRange.Count) return null;
  326. return triggersInRange[index].ranges[bestRangeIndexes[index]];
  327. }
  328. /// <summary>
  329. /// Gets the closest InteractionObject in range.
  330. /// </summary>
  331. public InteractionObject GetClosestInteractionObjectInRange() {
  332. var range = GetClosestInteractionRange();
  333. if (range == null) return null;
  334. return range.interactions[0].interactionObject;
  335. }
  336. /// <summary>
  337. /// Gets the closest InteractionTarget in range.
  338. /// </summary>
  339. public InteractionTarget GetClosestInteractionTargetInRange() {
  340. var range = GetClosestInteractionRange();
  341. if (range == null) return null;
  342. return range.interactions[0].interactionObject.GetTarget(range.interactions[0].effectors[0], this);
  343. }
  344. /// <summary>
  345. /// Gets the closest InteractionObjects in range.
  346. /// </summary>
  347. public InteractionObject[] GetClosestInteractionObjectsInRange() {
  348. var range = GetClosestInteractionRange();
  349. if (range == null) return new InteractionObject[0];
  350. InteractionObject[] objects = new InteractionObject[range.interactions.Length];
  351. for (int i = 0; i < range.interactions.Length; i++) {
  352. objects[i] = range.interactions[i].interactionObject;
  353. }
  354. return objects;
  355. }
  356. /// <summary>
  357. /// Gets the closest InteractionTargets in range.
  358. /// </summary>
  359. public InteractionTarget[] GetClosestInteractionTargetsInRange() {
  360. var range = GetClosestInteractionRange();
  361. if (range == null) return new InteractionTarget[0];
  362. List<InteractionTarget> targets = new List<InteractionTarget>();
  363. foreach (InteractionTrigger.Range.Interaction interaction in range.interactions) {
  364. foreach (FullBodyBipedEffector effectorType in interaction.effectors) {
  365. targets.Add (interaction.interactionObject.GetTarget(effectorType, this));
  366. }
  367. }
  368. return (InteractionTarget[])targets.ToArray();
  369. }
  370. /// <summary>
  371. /// Returns true if all effectors of a trigger are either not in interaction or paused
  372. /// </summary>
  373. public bool TriggerEffectorsReady(int index) {
  374. if (!IsValid(true)) return false;
  375. if (!TriggerIndexIsValid(index)) return false;
  376. for (int r = 0; r < triggersInRange[index].ranges.Length; r++) {
  377. var range = triggersInRange[index].ranges[r];
  378. for (int i = 0; i < range.interactions.Length; i++) {
  379. for (int e = 0; e < range.interactions[i].effectors.Length; e++) {
  380. if (IsInInteraction(range.interactions[i].effectors[e])) return false;
  381. }
  382. }
  383. for (int i = 0; i < range.interactions.Length; i++) {
  384. for (int e = 0; e < range.interactions[i].effectors.Length; e++) {
  385. if (IsPaused(range.interactions[i].effectors[e])) {
  386. for (int n = 0; n < range.interactions[i].effectors.Length; n++) {
  387. if (n != e && !IsPaused(range.interactions[i].effectors[n])) return false;
  388. }
  389. }
  390. }
  391. }
  392. }
  393. return true;
  394. }
  395. /// <summary>
  396. /// Return the current most appropriate range of an InteractionTrigger listed in triggersInRange.
  397. /// </summary>
  398. public InteractionTrigger.Range GetTriggerRange(int index) {
  399. if (!IsValid(true)) return null;
  400. if (index < 0 || index >= bestRangeIndexes.Count) {
  401. Warning.Log("Index out of range.", transform);
  402. return null;
  403. }
  404. return triggersInRange[index].ranges[bestRangeIndexes[index]];
  405. }
  406. /// <summary>
  407. /// Returns the InteractionTrigger that is in range and closest to the character.
  408. /// </summary>
  409. public int GetClosestTriggerIndex() {
  410. if (!IsValid(true)) return -1;
  411. if (triggersInRange.Count == 0) return -1;
  412. if (triggersInRange.Count == 1) return 0;
  413. int closest = -1;
  414. float closestSqrMag = Mathf.Infinity;
  415. for (int i = 0; i < triggersInRange.Count; i++) {
  416. if (triggersInRange[i] != null) {
  417. float sqrMag = Vector3.SqrMagnitude(triggersInRange[i].transform.position - transform.position);
  418. if (sqrMag < closestSqrMag) {
  419. closest = i;
  420. closestSqrMag = sqrMag;
  421. }
  422. }
  423. }
  424. return closest;
  425. }
  426. /// <summary>
  427. /// Gets the FullBodyBipedIK component.
  428. /// </summary>
  429. public FullBodyBipedIK ik {
  430. get {
  431. return fullBody;
  432. }
  433. set {
  434. fullBody = value;
  435. }
  436. }
  437. /// <summary>
  438. /// Gets the in contact.
  439. /// </summary>
  440. /// <value>The in contact.</value>
  441. public List<InteractionTrigger> triggersInRange { get; private set; }
  442. private List<InteractionTrigger> inContact = new List<InteractionTrigger>();
  443. private List<int> bestRangeIndexes = new List<int>();
  444. /// <summary>
  445. /// Interaction delegate
  446. /// </summary>
  447. public delegate void InteractionDelegate(FullBodyBipedEffector effectorType, InteractionObject interactionObject);
  448. /// <summary>
  449. /// Interaction event delegate
  450. /// </summary>
  451. public delegate void InteractionEventDelegate(FullBodyBipedEffector effectorType, InteractionObject interactionObject, InteractionObject.InteractionEvent interactionEvent);
  452. /// <summary>
  453. /// Called when an InteractionEvent has been started
  454. /// </summary>
  455. public InteractionDelegate OnInteractionStart;
  456. /// <summary>
  457. /// Called when an Interaction has been paused
  458. /// </summary>
  459. public InteractionDelegate OnInteractionPause;
  460. /// <summary>
  461. /// Called when an InteractionObject has been picked up.
  462. /// </summary>
  463. public InteractionDelegate OnInteractionPickUp;
  464. /// <summary>
  465. /// Called when a paused Interaction has been resumed
  466. /// </summary>
  467. public InteractionDelegate OnInteractionResume;
  468. /// <summary>
  469. /// Called when an Interaction has been stopped
  470. /// </summary>
  471. public InteractionDelegate OnInteractionStop;
  472. /// <summary>
  473. /// Called when an interaction event occurs.
  474. /// </summary>
  475. public InteractionEventDelegate OnInteractionEvent;
  476. /// <summary>
  477. /// Gets the RaycastHit from trigger seeking.
  478. /// </summary>
  479. /// <value>The hit.</value>
  480. public RaycastHit raycastHit;
  481. #endregion Main Interface
  482. [Space(10)]
  483. [Tooltip("Reference to the FBBIK component.")]
  484. [SerializeField] FullBodyBipedIK fullBody; // Reference to the FBBIK component.
  485. /// <summary>
  486. /// Handles looking at the interactions.
  487. /// </summary>
  488. [Tooltip("Handles looking at the interactions.")]
  489. public InteractionLookAt lookAt = new InteractionLookAt();
  490. // The array of Interaction Effectors
  491. private InteractionEffector[] interactionEffectors = new InteractionEffector[9] {
  492. new InteractionEffector(FullBodyBipedEffector.Body),
  493. new InteractionEffector(FullBodyBipedEffector.LeftFoot),
  494. new InteractionEffector(FullBodyBipedEffector.LeftHand),
  495. new InteractionEffector(FullBodyBipedEffector.LeftShoulder),
  496. new InteractionEffector(FullBodyBipedEffector.LeftThigh),
  497. new InteractionEffector(FullBodyBipedEffector.RightFoot),
  498. new InteractionEffector(FullBodyBipedEffector.RightHand),
  499. new InteractionEffector(FullBodyBipedEffector.RightShoulder),
  500. new InteractionEffector(FullBodyBipedEffector.RightThigh)
  501. };
  502. public bool initiated { get; private set; }
  503. private Collider lastCollider, c;
  504. // Initiate
  505. public void Start() {
  506. if (fullBody == null) fullBody = GetComponent<FullBodyBipedIK>();
  507. //Debug.Log(fullBody);
  508. if (fullBody == null) {
  509. Warning.Log("InteractionSystem can not find a FullBodyBipedIK component", transform);
  510. return;
  511. }
  512. // Add to the FBBIK OnPostUpdate delegate to get a call when it has finished updating
  513. fullBody.solver.OnPreUpdate += OnPreFBBIK;
  514. fullBody.solver.OnPostUpdate += OnPostFBBIK;
  515. fullBody.solver.OnFixTransforms += OnFixTransforms;
  516. OnInteractionStart += LookAtInteraction;
  517. OnInteractionPause += InteractionPause;
  518. OnInteractionResume += InteractionResume;
  519. OnInteractionStop += InteractionStop;
  520. foreach (InteractionEffector e in interactionEffectors) e.Initiate(this);
  521. triggersInRange = new List<InteractionTrigger>();
  522. c = GetComponent<Collider>();
  523. UpdateTriggerEventBroadcasting();
  524. initiated = true;
  525. }
  526. private void InteractionPause(FullBodyBipedEffector effector, InteractionObject interactionObject) {
  527. lookAt.isPaused = true;
  528. }
  529. private void InteractionResume(FullBodyBipedEffector effector, InteractionObject interactionObject) {
  530. lookAt.isPaused = false;
  531. }
  532. private void InteractionStop(FullBodyBipedEffector effector, InteractionObject interactionObject) {
  533. lookAt.isPaused = false;
  534. }
  535. // Called by the delegate
  536. private void LookAtInteraction(FullBodyBipedEffector effector, InteractionObject interactionObject) {
  537. lookAt.Look(interactionObject.lookAtTarget, Time.time + (interactionObject.length * 0.5f));
  538. }
  539. public void OnTriggerEnter(Collider c) {
  540. if (fullBody == null) return;
  541. var trigger = c.GetComponent<InteractionTrigger>();
  542. if (trigger == null) return;
  543. if (inContact.Contains(trigger)) return;
  544. inContact.Add(trigger);
  545. }
  546. public void OnTriggerExit(Collider c) {
  547. if (fullBody == null) return;
  548. var trigger = c.GetComponent<InteractionTrigger>();
  549. if (trigger == null) return;
  550. inContact.Remove(trigger);
  551. }
  552. // Is the InteractionObject trigger in range of any effectors? If the trigger collider is bigger than any effector ranges, then the object in contact is still unreachable.
  553. private bool ContactIsInRange(int index, out int bestRangeIndex) {
  554. bestRangeIndex = -1;
  555. if (!IsValid(true)) return false;
  556. if (index < 0 || index >= inContact.Count) {
  557. Warning.Log("Index out of range.", transform);
  558. return false;
  559. }
  560. if (inContact[index] == null) {
  561. Warning.Log("The InteractionTrigger in the list 'inContact' has been destroyed", transform);
  562. return false;
  563. }
  564. bestRangeIndex = inContact[index].GetBestRangeIndex(transform, FPSCamera, raycastHit);
  565. if (bestRangeIndex == -1) return false;
  566. return true;
  567. }
  568. // Using this to assign some default values in Editor
  569. void OnDrawGizmosSelected() {
  570. if (Application.isPlaying) return;
  571. if (fullBody == null) fullBody = GetComponent<FullBodyBipedIK>();
  572. if (characterCollider == null) characterCollider = GetComponent<Collider>();
  573. }
  574. public void Update() {
  575. if (fullBody == null) return;
  576. UpdateTriggerEventBroadcasting();
  577. Raycasting();
  578. // Finding the triggers in contact and in range
  579. triggersInRange.Clear();
  580. bestRangeIndexes.Clear();
  581. for (int i = 0; i < inContact.Count; i++) {
  582. int bestRangeIndex = -1;
  583. if (inContact[i] != null && inContact[i].gameObject.activeInHierarchy && ContactIsInRange(i, out bestRangeIndex)) {
  584. triggersInRange.Add(inContact[i]);
  585. bestRangeIndexes.Add(bestRangeIndex);
  586. }
  587. }
  588. // Update LookAt
  589. lookAt.Update();
  590. }
  591. // Finds triggers that need camera position and rotation
  592. private void Raycasting() {
  593. if (camRaycastLayers == -1) return;
  594. if (FPSCamera == null) return;
  595. Physics.Raycast(FPSCamera.position, FPSCamera.forward, out raycastHit, camRaycastDistance, camRaycastLayers);
  596. }
  597. // Update collider and TriggerEventBroadcaster
  598. private void UpdateTriggerEventBroadcasting() {
  599. if (characterCollider == null) characterCollider = c;
  600. if (characterCollider != null && characterCollider != c) {
  601. if (characterCollider.GetComponent<TriggerEventBroadcaster>() == null) {
  602. var t = characterCollider.gameObject.AddComponent<TriggerEventBroadcaster>();
  603. t.target = gameObject;
  604. }
  605. if (lastCollider != null && lastCollider != c && lastCollider != characterCollider) {
  606. var t = lastCollider.GetComponent<TriggerEventBroadcaster>();
  607. if (t != null) Destroy(t);
  608. }
  609. }
  610. lastCollider = characterCollider;
  611. }
  612. // Update the interaction
  613. void UpdateEffectors() {
  614. if (fullBody == null) return;
  615. for (int i = 0; i < interactionEffectors.Length; i++) interactionEffectors[i].Update(transform, speed);
  616. // Interpolate to default pull, reach values
  617. for (int i = 0; i < interactionEffectors.Length; i++) interactionEffectors[i].ResetToDefaults(resetToDefaultsSpeed * speed);
  618. }
  619. // Used for using LookAtIK to rotate the spine
  620. private void OnPreFBBIK() {
  621. //if (!enabled) return;
  622. if (fullBody == null) return;
  623. lookAt.SolveSpine();
  624. UpdateEffectors();
  625. }
  626. // Used for rotating the hands after FBBIK has finished
  627. private void OnPostFBBIK() {
  628. //if (!enabled) return;
  629. if (fullBody == null) return;
  630. for (int i = 0; i < interactionEffectors.Length; i++) interactionEffectors[i].OnPostFBBIK();
  631. // Update LookAtIK head
  632. lookAt.SolveHead();
  633. }
  634. void OnFixTransforms() {
  635. lookAt.OnFixTransforms();
  636. }
  637. // Remove the delegates
  638. void OnDestroy() {
  639. if (fullBody == null) return;
  640. fullBody.solver.OnPreUpdate -= OnPreFBBIK;
  641. fullBody.solver.OnPostUpdate -= OnPostFBBIK;
  642. fullBody.solver.OnFixTransforms -= OnFixTransforms;
  643. OnInteractionStart -= LookAtInteraction;
  644. OnInteractionPause -= InteractionPause;
  645. OnInteractionResume -= InteractionResume;
  646. OnInteractionStop -= InteractionStop;
  647. }
  648. // Is this InteractionSystem valid and initiated
  649. private bool IsValid(bool log) {
  650. if (fullBody == null) {
  651. if (log) Warning.Log("FBBIK is null. Will not update the InteractionSystem", transform);
  652. return false;
  653. }
  654. if (!initiated) {
  655. if (log) Warning.Log("The InteractionSystem has not been initiated yet.", transform);
  656. return false;
  657. }
  658. return true;
  659. }
  660. // Is the index of triggersInRange valid?
  661. private bool TriggerIndexIsValid(int index) {
  662. if (index < 0 || index >= triggersInRange.Count) {
  663. Warning.Log("Index out of range.", transform);
  664. return false;
  665. }
  666. if (triggersInRange[index] == null) {
  667. Warning.Log("The InteractionTrigger in the list 'inContact' has been destroyed", transform);
  668. return false;
  669. }
  670. return true;
  671. }
  672. }
  673. }