Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

348 lines
14 KiB

  1. // Player Climb|Locomotion|20120
  2. namespace VRTK
  3. {
  4. using GrabAttachMechanics;
  5. using UnityEngine;
  6. /// <summary>
  7. /// Event Payload
  8. /// </summary>
  9. /// <param name="controllerReference">The reference to the controller doing the interaction.</param>
  10. /// <param name="target">The GameObject of the interactable object that is being interacted with by the controller.</param>
  11. public struct PlayerClimbEventArgs
  12. {
  13. public VRTK_ControllerReference controllerReference;
  14. public GameObject target;
  15. }
  16. /// <summary>
  17. /// Event Payload
  18. /// </summary>
  19. /// <param name="sender">this object</param>
  20. /// <param name="e"><see cref="PlayerClimbEventArgs"/></param>
  21. public delegate void PlayerClimbEventHandler(object sender, PlayerClimbEventArgs e);
  22. /// <summary>
  23. /// Provides the ability for the SDK Camera Rig to be moved around based on whether an Interact Grab is interacting with a Climbable Interactable Object to simulate climbing.
  24. /// </summary>
  25. /// <remarks>
  26. /// **Required Components:**
  27. /// * `VRTK_BodyPhysics` - A Body Physics script to deal with the effects of physics and gravity on the play area.
  28. ///
  29. /// **Optional Components:**
  30. /// * `VRTK_BasicTeleport` - A Teleporter script to use when snapping the play area to the nearest floor when releasing from grab.
  31. /// * `VRTK_HeadsetCollision` - A Headset Collision script to determine when the headset is colliding with geometry to know when to reset to a valid location.
  32. /// * `VRTK_PositionRewind` - A Position Rewind script to utilise when resetting to a valid location upon ungrabbing whilst colliding with geometry.
  33. ///
  34. /// **Script Usage:**
  35. /// * Place the `VRTK_PlayerClimb` script on any active scene GameObject.
  36. ///
  37. /// **Script Dependencies:**
  38. /// * The controller Script Alias GameObject requires the Interact Touch and Interact Grab scripts to allow for touching and grabbing of Interactable Objects.
  39. /// * An Interactable Object in the scene that has the Climbable Grab Attach Mechanic.
  40. /// </remarks>
  41. /// <example>
  42. /// `VRTK/Examples/037_CameraRig_ClimbingFalling` shows how to set up a scene with player climbing. There are many different examples showing how the same system can be used in unique ways.
  43. /// </example>
  44. [AddComponentMenu("VRTK/Scripts/Locomotion/VRTK_PlayerClimb")]
  45. public class VRTK_PlayerClimb : MonoBehaviour
  46. {
  47. [Header("Climb Settings")]
  48. [Tooltip("Will scale movement up and down based on the player transform's scale.")]
  49. public bool usePlayerScale = true;
  50. [Header("Custom Settings")]
  51. [Tooltip("The Body Physics script to use for dealing with climbing and falling. If this is left blank then the script will need to be applied to the same GameObject.")]
  52. public VRTK_BodyPhysics bodyPhysics;
  53. [Tooltip("The Teleport script to use when snapping to nearest floor on release. If this is left blank then a Teleport script will need to be applied to the same GameObject.")]
  54. public VRTK_BasicTeleport teleporter;
  55. [Tooltip("The Headset Collision script to use for determining if the user is climbing inside a collidable object. If this is left blank then the script will need to be applied to the same GameObject.")]
  56. public VRTK_HeadsetCollision headsetCollision;
  57. [Tooltip("The Position Rewind script to use for dealing resetting invalid positions. If this is left blank then the script will need to be applied to the same GameObject.")]
  58. public VRTK_PositionRewind positionRewind;
  59. /// <summary>
  60. /// Emitted when player climbing has started.
  61. /// </summary>
  62. public event PlayerClimbEventHandler PlayerClimbStarted;
  63. /// <summary>
  64. /// Emitted when player climbing has ended.
  65. /// </summary>
  66. public event PlayerClimbEventHandler PlayerClimbEnded;
  67. protected Transform playArea;
  68. protected Vector3 startControllerScaledLocalPosition;
  69. protected Vector3 startGrabPointLocalPosition;
  70. protected Vector3 startPlayAreaWorldOffset;
  71. protected GameObject grabbingController;
  72. protected GameObject climbingObject;
  73. protected Quaternion climbingObjectLastRotation;
  74. protected bool isClimbing;
  75. protected bool useGrabbedObjectRotation;
  76. /// <summary>
  77. /// The IsClimbing method will return if climbing is currently taking place or not.
  78. /// </summary>
  79. /// <returns>Returns `true` if climbing is currently taking place.</returns>
  80. public virtual bool IsClimbing()
  81. {
  82. return isClimbing;
  83. }
  84. protected virtual void Awake()
  85. {
  86. bodyPhysics = (bodyPhysics != null ? bodyPhysics : FindObjectOfType<VRTK_BodyPhysics>());
  87. if (bodyPhysics == null)
  88. {
  89. VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_SCENE, "VRTK_PlayerClimb", "VRTK_BodyPhysics"));
  90. }
  91. teleporter = (teleporter != null ? teleporter : FindObjectOfType<VRTK_BasicTeleport>());
  92. headsetCollision = (headsetCollision != null ? headsetCollision : FindObjectOfType<VRTK_HeadsetCollision>());
  93. positionRewind = (positionRewind != null ? positionRewind : FindObjectOfType<VRTK_PositionRewind>());
  94. VRTK_SDKManager.AttemptAddBehaviourToToggleOnLoadedSetupChange(this);
  95. }
  96. protected virtual void OnEnable()
  97. {
  98. playArea = VRTK_DeviceFinder.PlayAreaTransform();
  99. InitListeners(true);
  100. }
  101. protected virtual void OnDisable()
  102. {
  103. Ungrab(false, null, climbingObject);
  104. InitListeners(false);
  105. }
  106. protected virtual void OnDestroy()
  107. {
  108. VRTK_SDKManager.AttemptRemoveBehaviourToToggleOnLoadedSetupChange(this);
  109. }
  110. protected virtual void Update()
  111. {
  112. if (isClimbing)
  113. {
  114. Vector3 controllerLocalOffset = GetScaledLocalPosition(grabbingController.transform) - startControllerScaledLocalPosition;
  115. Vector3 grabPointWorldPosition = climbingObject.transform.TransformPoint(startGrabPointLocalPosition);
  116. playArea.position = grabPointWorldPosition + startPlayAreaWorldOffset - controllerLocalOffset;
  117. if (useGrabbedObjectRotation)
  118. {
  119. Vector3 lastRotationVec = climbingObjectLastRotation * Vector3.forward;
  120. Vector3 currentObectRotationVec = climbingObject.transform.rotation * Vector3.forward;
  121. Vector3 axis = Vector3.Cross(lastRotationVec, currentObectRotationVec);
  122. float angle = Vector3.Angle(lastRotationVec, currentObectRotationVec);
  123. playArea.RotateAround(grabPointWorldPosition, axis, angle);
  124. climbingObjectLastRotation = climbingObject.transform.rotation;
  125. }
  126. if (positionRewind != null && !IsHeadsetColliding())
  127. {
  128. positionRewind.SetLastGoodPosition();
  129. }
  130. }
  131. }
  132. protected virtual void OnPlayerClimbStarted(PlayerClimbEventArgs e)
  133. {
  134. if (PlayerClimbStarted != null)
  135. {
  136. PlayerClimbStarted(this, e);
  137. }
  138. }
  139. protected virtual void OnPlayerClimbEnded(PlayerClimbEventArgs e)
  140. {
  141. if (PlayerClimbEnded != null)
  142. {
  143. PlayerClimbEnded(this, e);
  144. }
  145. }
  146. protected virtual PlayerClimbEventArgs SetPlayerClimbEvent(VRTK_ControllerReference controllerReference, GameObject target)
  147. {
  148. PlayerClimbEventArgs e;
  149. e.controllerReference = controllerReference;
  150. e.target = target;
  151. return e;
  152. }
  153. protected virtual void InitListeners(bool state)
  154. {
  155. InitControllerListeners(VRTK_DeviceFinder.GetControllerLeftHand(), state);
  156. InitControllerListeners(VRTK_DeviceFinder.GetControllerRightHand(), state);
  157. InitTeleportListener(state);
  158. }
  159. protected virtual void InitTeleportListener(bool state)
  160. {
  161. if (teleporter != null)
  162. {
  163. if (state)
  164. {
  165. teleporter.Teleporting += new TeleportEventHandler(OnTeleport);
  166. }
  167. else
  168. {
  169. teleporter.Teleporting -= new TeleportEventHandler(OnTeleport);
  170. }
  171. }
  172. }
  173. protected virtual void OnTeleport(object sender, DestinationMarkerEventArgs e)
  174. {
  175. if (isClimbing)
  176. {
  177. Ungrab(false, e.controllerReference, e.target.gameObject);
  178. }
  179. }
  180. protected virtual Vector3 GetScaledLocalPosition(Transform objTransform)
  181. {
  182. if (usePlayerScale)
  183. {
  184. return (playArea.localRotation * Vector3.Scale(objTransform.localPosition, playArea.localScale));
  185. }
  186. return (playArea.localRotation * objTransform.localPosition);
  187. }
  188. protected virtual void OnGrabObject(object sender, ObjectInteractEventArgs e)
  189. {
  190. if (IsClimbableObject(e.target))
  191. {
  192. GameObject controller = ((VRTK_InteractGrab)sender).gameObject;
  193. GameObject actualController = VRTK_DeviceFinder.GetActualController(controller);
  194. Grab(actualController, e.controllerReference, e.target);
  195. }
  196. }
  197. protected virtual void OnUngrabObject(object sender, ObjectInteractEventArgs e)
  198. {
  199. GameObject controller = ((VRTK_InteractGrab)sender).gameObject;
  200. GameObject actualController = VRTK_DeviceFinder.GetActualController(controller);
  201. if (e.target != null && IsClimbableObject(e.target) && IsActiveClimbingController(actualController))
  202. {
  203. Ungrab(true, e.controllerReference, e.target);
  204. }
  205. }
  206. protected virtual void Grab(GameObject currentGrabbingController, VRTK_ControllerReference controllerReference, GameObject target)
  207. {
  208. if (bodyPhysics == null)
  209. {
  210. return;
  211. }
  212. bodyPhysics.ResetFalling();
  213. bodyPhysics.TogglePreventSnapToFloor(true);
  214. bodyPhysics.enableBodyCollisions = false;
  215. bodyPhysics.ToggleOnGround(false);
  216. isClimbing = true;
  217. climbingObject = target;
  218. grabbingController = currentGrabbingController;
  219. startControllerScaledLocalPosition = GetScaledLocalPosition(grabbingController.transform);
  220. startGrabPointLocalPosition = climbingObject.transform.InverseTransformPoint(grabbingController.transform.position);
  221. startPlayAreaWorldOffset = playArea.transform.position - grabbingController.transform.position;
  222. climbingObjectLastRotation = climbingObject.transform.rotation;
  223. useGrabbedObjectRotation = climbingObject.GetComponent<VRTK_ClimbableGrabAttach>().useObjectRotation;
  224. OnPlayerClimbStarted(SetPlayerClimbEvent(controllerReference, climbingObject));
  225. }
  226. protected virtual void Ungrab(bool carryMomentum, VRTK_ControllerReference controllerReference, GameObject target)
  227. {
  228. if (bodyPhysics == null)
  229. {
  230. return;
  231. }
  232. isClimbing = false;
  233. if (positionRewind != null && IsHeadsetColliding())
  234. {
  235. positionRewind.RewindPosition();
  236. }
  237. if (IsBodyColliding() && !IsHeadsetColliding())
  238. {
  239. bodyPhysics.ForceSnapToFloor();
  240. }
  241. bodyPhysics.enableBodyCollisions = true;
  242. if (carryMomentum)
  243. {
  244. Vector3 velocity = Vector3.zero;
  245. if (VRTK_ControllerReference.IsValid(controllerReference))
  246. {
  247. velocity = -VRTK_DeviceFinder.GetControllerVelocity(controllerReference);
  248. if (usePlayerScale)
  249. {
  250. velocity = playArea.TransformVector(velocity);
  251. }
  252. else
  253. {
  254. velocity = playArea.TransformDirection(velocity);
  255. }
  256. }
  257. bodyPhysics.ApplyBodyVelocity(velocity, true, true);
  258. }
  259. grabbingController = null;
  260. climbingObject = null;
  261. OnPlayerClimbEnded(SetPlayerClimbEvent(controllerReference, target));
  262. }
  263. protected virtual bool IsActiveClimbingController(GameObject controller)
  264. {
  265. return (controller == grabbingController);
  266. }
  267. protected virtual bool IsClimbableObject(GameObject obj)
  268. {
  269. VRTK_InteractableObject interactObject = obj.GetComponent<VRTK_InteractableObject>();
  270. return (interactObject != null && interactObject.grabAttachMechanicScript && interactObject.grabAttachMechanicScript.IsClimbable());
  271. }
  272. protected virtual void InitControllerListeners(GameObject controller, bool state)
  273. {
  274. if (controller != null)
  275. {
  276. VRTK_InteractGrab grabScript = controller.GetComponentInChildren<VRTK_InteractGrab>();
  277. if (grabScript != null)
  278. {
  279. if (state)
  280. {
  281. grabScript.ControllerGrabInteractableObject += new ObjectInteractEventHandler(OnGrabObject);
  282. grabScript.ControllerUngrabInteractableObject += new ObjectInteractEventHandler(OnUngrabObject);
  283. }
  284. else
  285. {
  286. grabScript.ControllerGrabInteractableObject -= new ObjectInteractEventHandler(OnGrabObject);
  287. grabScript.ControllerUngrabInteractableObject -= new ObjectInteractEventHandler(OnUngrabObject);
  288. }
  289. }
  290. }
  291. }
  292. protected virtual bool IsBodyColliding()
  293. {
  294. return (bodyPhysics != null && bodyPhysics.GetCurrentCollidingObject() != null);
  295. }
  296. protected virtual bool IsHeadsetColliding()
  297. {
  298. return (headsetCollision != null && headsetCollision.IsColliding());
  299. }
  300. }
  301. }