InteractionEffector.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using RootMotion;
  5. namespace RootMotion.FinalIK {
  6. /// <summary>
  7. /// Managing Interactions for a single FBBIK effector.
  8. /// </summary>
  9. [System.Serializable]
  10. public class InteractionEffector {
  11. // The type of the effector
  12. public FullBodyBipedEffector effectorType { get; private set; }
  13. // Has the interaction been paused?
  14. public bool isPaused { get; private set; }
  15. // The current InteractionObject (null if there is no interaction going on)
  16. public InteractionObject interactionObject { get; private set; }
  17. // Is this InteractionEffector currently in the middle of an interaction?
  18. public bool inInteraction { get { return interactionObject != null; }}
  19. // Internal values
  20. private Poser poser;
  21. private IKEffector effector;
  22. private float timer, length, weight, fadeInSpeed, defaultPositionWeight, defaultRotationWeight, defaultPull, defaultReach, defaultPush, defaultPushParent, defaultBendGoalWeight, resetTimer;
  23. private bool positionWeightUsed, rotationWeightUsed, pullUsed, reachUsed, pushUsed, pushParentUsed, bendGoalWeightUsed;
  24. private bool pickedUp, defaults, pickUpOnPostFBBIK;
  25. private Vector3 pickUpPosition, pausePositionRelative;
  26. private Quaternion pickUpRotation, pauseRotationRelative;
  27. private InteractionTarget interactionTarget;
  28. private Transform target;
  29. private List<bool> triggered = new List<bool>();
  30. private InteractionSystem interactionSystem;
  31. private bool started;
  32. // The custom constructor
  33. public InteractionEffector (FullBodyBipedEffector effectorType) {
  34. this.effectorType = effectorType;
  35. }
  36. // Initiate this, get the default values
  37. public void Initiate(InteractionSystem interactionSystem) {
  38. this.interactionSystem = interactionSystem;
  39. // Find the effector if we haven't already
  40. effector = interactionSystem.ik.solver.GetEffector(effectorType);
  41. poser = effector.bone.GetComponent<Poser>();
  42. StoreDefaults();
  43. }
  44. private void StoreDefaults() {
  45. defaultPositionWeight = interactionSystem.ik.solver.GetEffector(effectorType).positionWeight;
  46. defaultRotationWeight = interactionSystem.ik.solver.GetEffector(effectorType).rotationWeight;
  47. //defaultPoserWeight = poser != null? poser.weight: 0f;
  48. defaultPull = interactionSystem.ik.solver.GetChain(effectorType).pull;
  49. defaultReach = interactionSystem.ik.solver.GetChain(effectorType).reach;
  50. defaultPush = interactionSystem.ik.solver.GetChain(effectorType).push;
  51. defaultPushParent = interactionSystem.ik.solver.GetChain(effectorType).pushParent;
  52. defaultBendGoalWeight = interactionSystem.ik.solver.GetChain(effectorType).bendConstraint.weight;
  53. }
  54. // Interpolate to default values when currently not in interaction
  55. public bool ResetToDefaults(float speed) {
  56. if (inInteraction) return false;
  57. if (isPaused) return false;
  58. if (defaults) return false;
  59. resetTimer = Mathf.MoveTowards(resetTimer, 0f, Time.deltaTime * speed);
  60. // Pull and Reach
  61. if (effector.isEndEffector) {
  62. if (pullUsed) interactionSystem.ik.solver.GetChain(effectorType).pull = Mathf.Lerp(defaultPull, interactionSystem.ik.solver.GetChain(effectorType).pull, resetTimer);
  63. if (reachUsed) interactionSystem.ik.solver.GetChain(effectorType).reach = Mathf.Lerp(defaultReach, interactionSystem.ik.solver.GetChain(effectorType).reach, resetTimer);
  64. if (pushUsed) interactionSystem.ik.solver.GetChain(effectorType).push = Mathf.Lerp(defaultPush, interactionSystem.ik.solver.GetChain(effectorType).push, resetTimer);
  65. if (pushParentUsed) interactionSystem.ik.solver.GetChain(effectorType).pushParent = Mathf.Lerp(defaultPushParent, interactionSystem.ik.solver.GetChain(effectorType).pushParent, resetTimer);
  66. if (bendGoalWeightUsed) interactionSystem.ik.solver.GetChain(effectorType).bendConstraint.weight = Mathf.Lerp(defaultBendGoalWeight, interactionSystem.ik.solver.GetChain(effectorType).bendConstraint.weight, resetTimer);
  67. }
  68. // Effector weights
  69. if (positionWeightUsed) effector.positionWeight = Mathf.Lerp(defaultPositionWeight, effector.positionWeight, resetTimer);
  70. if (rotationWeightUsed) effector.rotationWeight = Mathf.Lerp(defaultRotationWeight, effector.rotationWeight, resetTimer);
  71. if (resetTimer <= 0f) {
  72. pullUsed = false;
  73. reachUsed = false;
  74. pushUsed = false;
  75. pushParentUsed = false;
  76. positionWeightUsed = false;
  77. rotationWeightUsed = false;
  78. bendGoalWeightUsed = false;
  79. //poserUsed = false;
  80. defaults = true;
  81. }
  82. return true;
  83. }
  84. // Pause this interaction
  85. public bool Pause() {
  86. if (!inInteraction) return false;
  87. isPaused = true;
  88. pausePositionRelative = target.InverseTransformPoint(effector.position);
  89. pauseRotationRelative = Quaternion.Inverse(target.rotation) * effector.rotation;
  90. if (interactionSystem.OnInteractionPause != null) {
  91. interactionSystem.OnInteractionPause(effectorType, interactionObject);
  92. }
  93. return true;
  94. }
  95. // Resume a paused interaction
  96. public bool Resume() {
  97. if (!inInteraction) return false;
  98. isPaused = false;
  99. if (interactionSystem.OnInteractionResume != null) interactionSystem.OnInteractionResume(effectorType, interactionObject);
  100. return true;
  101. }
  102. // Start interaction
  103. public bool Start(InteractionObject interactionObject, string tag, float fadeInTime, bool interrupt) {
  104. // If not in interaction, set effector positions to their bones
  105. if (!inInteraction) {
  106. effector.position = effector.bone.position;;
  107. effector.rotation = effector.bone.rotation;;
  108. } else {
  109. if (!interrupt) return false;
  110. else defaults = false;
  111. }
  112. // Get the InteractionTarget
  113. target = interactionObject.GetTarget(effectorType, tag);
  114. if (target == null) return false;
  115. interactionTarget = target.GetComponent<InteractionTarget>();
  116. // Start the interaction
  117. this.interactionObject = interactionObject;
  118. if (interactionSystem.OnInteractionStart != null) interactionSystem.OnInteractionStart(effectorType, interactionObject);
  119. interactionObject.OnStartInteraction(interactionSystem);
  120. // Cleared triggered events
  121. triggered.Clear();
  122. for (int i = 0; i < interactionObject.events.Length; i++) {
  123. triggered.Add(false);
  124. }
  125. // Posing the hand/foot
  126. if (poser != null) {
  127. if (poser.poseRoot == null) poser.weight = 0f;
  128. if (interactionTarget != null) poser.poseRoot = target.transform;
  129. else poser.poseRoot = null;
  130. poser.AutoMapping();
  131. }
  132. // See which InteractionObject.WeightCurve.Types are used
  133. positionWeightUsed = interactionObject.CurveUsed(InteractionObject.WeightCurve.Type.PositionWeight);
  134. rotationWeightUsed = interactionObject.CurveUsed(InteractionObject.WeightCurve.Type.RotationWeight);
  135. pullUsed = interactionObject.CurveUsed(InteractionObject.WeightCurve.Type.Pull);
  136. reachUsed = interactionObject.CurveUsed(InteractionObject.WeightCurve.Type.Reach);
  137. pushUsed = interactionObject.CurveUsed(InteractionObject.WeightCurve.Type.Push);
  138. pushParentUsed = interactionObject.CurveUsed(InteractionObject.WeightCurve.Type.PushParent);
  139. bendGoalWeightUsed = interactionObject.CurveUsed(InteractionObject.WeightCurve.Type.BendGoalWeight);
  140. //poserUsed = interactionObject.CurveUsed(InteractionObject.WeightCurve.Type.PoserWeight);
  141. if (defaults) StoreDefaults();
  142. // Reset internal values
  143. timer = 0f;
  144. weight = 0f;
  145. fadeInSpeed = fadeInTime > 0f? 1f / fadeInTime: 1000f;
  146. length = interactionObject.length;
  147. isPaused = false;
  148. pickedUp = false;
  149. pickUpPosition = Vector3.zero;
  150. pickUpRotation = Quaternion.identity;
  151. if (interactionTarget != null) interactionTarget.RotateTo(effector.bone);
  152. started = true;
  153. return true;
  154. }
  155. // Update the (possibly) ongoing interaction
  156. public void Update(Transform root, float speed) {
  157. if (!inInteraction) {
  158. // If the InteractionObject has been destroyed, reset to defaults
  159. if (started) {
  160. isPaused = false;
  161. pickedUp = false;
  162. defaults = false;
  163. resetTimer = 1f;
  164. started = false;
  165. }
  166. return;
  167. }
  168. // Rotate target
  169. if (interactionTarget != null && !interactionTarget.rotateOnce) interactionTarget.RotateTo(effector.bone);
  170. if (isPaused) {
  171. effector.position = target.TransformPoint(pausePositionRelative);
  172. effector.rotation = target.rotation * pauseRotationRelative;
  173. // Apply the current interaction state to the solver
  174. interactionObject.Apply(interactionSystem.ik.solver, effectorType, interactionTarget, timer, weight);
  175. return;
  176. }
  177. // Advance the interaction timer and weight
  178. timer += Time.deltaTime * speed * (interactionTarget != null? interactionTarget.interactionSpeedMlp: 1f);
  179. weight = Mathf.Clamp(weight + Time.deltaTime * fadeInSpeed * speed, 0f, 1f);
  180. // Interaction events
  181. bool pickUp = false;
  182. bool pause = false;
  183. TriggerUntriggeredEvents(true, out pickUp, out pause);
  184. // Effector target positions and rotations
  185. Vector3 targetPosition = pickedUp? interactionSystem.transform.TransformPoint(pickUpPosition): target.position;
  186. Quaternion targetRotation = pickedUp? interactionSystem.transform.rotation * pickUpRotation: target.rotation;
  187. // Interpolate effector position and rotation
  188. effector.position = Vector3.Lerp(effector.bone.position, targetPosition, weight);
  189. effector.rotation = Quaternion.Lerp(effector.bone.rotation, targetRotation, weight);
  190. // Apply the current interaction state to the solver
  191. interactionObject.Apply(interactionSystem.ik.solver, effectorType, interactionTarget, timer, weight);
  192. if (pickUp) PickUp(root);
  193. if (pause) Pause();
  194. // Hand poser weight
  195. float poserWeight = interactionObject.GetValue (InteractionObject.WeightCurve.Type.PoserWeight, interactionTarget, timer);
  196. if (poser != null) {
  197. poser.weight = Mathf.Lerp (poser.weight, poserWeight, weight);
  198. } else {
  199. if (poserWeight > 0f) {
  200. Warning.Log("InteractionObject " + interactionObject.name + " has a curve/multipler for Poser Weight, but the bone of effector " + effectorType.ToString() + " has no HandPoser/GenericPoser attached.", effector.bone);
  201. }
  202. }
  203. if (timer >= length) Stop();
  204. }
  205. // Get the normalized progress of the interaction
  206. public float progress {
  207. get {
  208. if (!inInteraction) return 0f;
  209. if (length == 0f) return 0f;
  210. return timer / length;
  211. }
  212. }
  213. // Go through all the InteractionObject events to trigger the ones that have not yet triggered
  214. private void TriggerUntriggeredEvents(bool checkTime, out bool pickUp, out bool pause) {
  215. pickUp = false;
  216. pause = false;
  217. for (int i = 0; i < triggered.Count; i++) {
  218. // If this event has not been triggered by this effector
  219. if (!triggered[i]) {
  220. // If time has passed...
  221. if (!checkTime || interactionObject.events[i].time < timer) {
  222. // Activate the event
  223. interactionObject.events[i].Activate(effector.bone);
  224. // Picking up
  225. if (interactionObject.events[i].pickUp) {
  226. if (timer >= interactionObject.events[i].time) timer = interactionObject.events[i].time;
  227. pickUp = true;
  228. }
  229. // Pausing
  230. if (interactionObject.events[i].pause) {
  231. if (timer >= interactionObject.events[i].time) timer = interactionObject.events[i].time;
  232. pause = true;
  233. }
  234. if (interactionSystem.OnInteractionEvent != null) interactionSystem.OnInteractionEvent(effectorType, interactionObject, interactionObject.events[i]);
  235. triggered[i] = true;
  236. }
  237. }
  238. }
  239. }
  240. // Trigger the interaction object
  241. private void PickUp(Transform root) {
  242. // Picking up the object
  243. pickUpPosition = root.InverseTransformPoint(effector.position);
  244. pickUpRotation = Quaternion.Inverse(interactionSystem.transform.rotation) * effector.rotation;
  245. pickUpOnPostFBBIK = true;
  246. pickedUp = true;
  247. var rigidbody = interactionObject.targetsRoot.GetComponent<Rigidbody>();
  248. if (rigidbody != null) {
  249. if (!rigidbody.isKinematic) {
  250. rigidbody.isKinematic = true;
  251. }
  252. // Ignore collisions between the character and the colliders of the interaction object
  253. var rootCollider = root.GetComponent<Collider>();
  254. if (rootCollider != null) {
  255. var colliders = interactionObject.targetsRoot.GetComponentsInChildren<Collider>();
  256. foreach (Collider collider in colliders) {
  257. if (!collider.isTrigger && collider.enabled) Physics.IgnoreCollision(rootCollider, collider);
  258. }
  259. }
  260. }
  261. if (interactionSystem.OnInteractionPickUp != null) interactionSystem.OnInteractionPickUp(effectorType, interactionObject);
  262. }
  263. // Stop the interaction
  264. public bool Stop() {
  265. if (!inInteraction) return false;
  266. bool pickUp = false;
  267. bool pause = false;
  268. TriggerUntriggeredEvents(false, out pickUp, out pause);
  269. if (interactionSystem.OnInteractionStop != null) interactionSystem.OnInteractionStop(effectorType, interactionObject);
  270. // Reset the interaction target
  271. if (interactionTarget != null) interactionTarget.ResetRotation();
  272. // Reset the internal values
  273. interactionObject = null;
  274. weight = 0f;
  275. timer = 0f;
  276. isPaused = false;
  277. target = null;
  278. defaults = false;
  279. resetTimer = 1f;
  280. if (poser != null && !pickedUp) poser.weight = 0f;
  281. pickedUp = false;
  282. started = false;
  283. return true;
  284. }
  285. // Called after FBBIK update
  286. public void OnPostFBBIK() {
  287. if (!inInteraction) return;
  288. // Rotate the hands/feet to the RotateBoneWeight curve
  289. float rotateBoneWeight = interactionObject.GetValue(InteractionObject.WeightCurve.Type.RotateBoneWeight, interactionTarget, timer) * weight;
  290. if (rotateBoneWeight > 0f) {
  291. Quaternion r = pickedUp? interactionSystem.transform.rotation * pickUpRotation: effector.rotation;
  292. Quaternion targetRotation = Quaternion.Slerp(effector.bone.rotation, r, rotateBoneWeight * rotateBoneWeight);
  293. effector.bone.localRotation = Quaternion.Inverse(effector.bone.parent.rotation) * targetRotation;
  294. }
  295. // Positioning the interaction object to the effector (not the bone, because it is still at it's animated translation)
  296. if (pickUpOnPostFBBIK) {
  297. Vector3 bonePosition = effector.bone.position;
  298. effector.bone.position = interactionSystem.transform.TransformPoint(pickUpPosition);
  299. interactionObject.targetsRoot.parent = effector.bone;
  300. effector.bone.position = bonePosition;
  301. pickUpOnPostFBBIK = false;
  302. }
  303. }
  304. }
  305. }