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.

269 lines
11 KiB

  1. // Wheel|Controls3D|100062
  2. namespace VRTK
  3. {
  4. using UnityEngine;
  5. using GrabAttachMechanics;
  6. /// <summary>
  7. /// Attaching the script to a game object will allow the user to interact with it as if it were a spinnable wheel.
  8. /// </summary>
  9. /// <remarks>
  10. /// The script will instantiate the required Rigidbody and Interactable components automatically in case they do not exist yet.
  11. /// </remarks>
  12. /// <example>
  13. /// `VRTK/Examples/025_Controls_Overview` has a collection of wheels that can be rotated by grabbing with the controller and then rotating the controller in the desired direction.
  14. /// </example>
  15. [AddComponentMenu("VRTK/Scripts/Controls/3D/VRTK_Wheel")]
  16. [System.Obsolete("`VRTK.VRTK_Wheel` has been deprecated and can be recreated with `VRTK.Controllables.ArtificialBased.VRTK_ArtificialRotator`. This script will be removed in a future version of VRTK.")]
  17. public class VRTK_Wheel : VRTK_Control
  18. {
  19. /// <summary>
  20. /// The grab attach mechanic to use.
  21. /// </summary>
  22. public enum GrabTypes
  23. {
  24. /// <summary>
  25. /// Utilise the track object grab mechanic.
  26. /// </summary>
  27. TrackObject,
  28. /// <summary>
  29. /// Utilise the rotator track grab mechanic.
  30. /// </summary>
  31. RotatorTrack
  32. }
  33. [Tooltip("An optional game object to which the wheel will be connected. If the game object moves the wheel will follow along.")]
  34. public GameObject connectedTo;
  35. [Tooltip("The grab attach mechanic to use. Track Object allows for rotations of the controller, Rotator Track allows for grabbing the wheel and spinning it.")]
  36. public GrabTypes grabType = GrabTypes.TrackObject;
  37. [Tooltip("The maximum distance the grabbing controller is away from the wheel before it is automatically released.")]
  38. public float detatchDistance = 0.5f;
  39. [Tooltip("The minimum value the wheel can be set to.")]
  40. public float minimumValue = 0f;
  41. [Tooltip("The maximum value the wheel can be set to.")]
  42. public float maximumValue = 10f;
  43. [Tooltip("The increments in which values can change.")]
  44. public float stepSize = 1f;
  45. [Tooltip("If this is checked then when the wheel is released, it will snap to the step rotation.")]
  46. public bool snapToStep = false;
  47. [Tooltip("The amount of friction the wheel will have when it is grabbed.")]
  48. public float grabbedFriction = 25f;
  49. [Tooltip("The amount of friction the wheel will have when it is released.")]
  50. public float releasedFriction = 10f;
  51. [Range(0f, 359f)]
  52. [Tooltip("The maximum angle the wheel has to be turned to reach it's maximum value.")]
  53. public float maxAngle = 359f;
  54. [Tooltip("If this is checked then the wheel cannot be turned beyond the minimum and maximum value.")]
  55. public bool lockAtLimits = false;
  56. protected float angularVelocityLimit = 150f;
  57. protected float springStrengthValue = 150f;
  58. protected float springDamperValue = 5f;
  59. protected Quaternion initialLocalRotation;
  60. protected Rigidbody wheelRigidbody;
  61. protected HingeJoint wheelHinge;
  62. protected bool wheelHingeCreated = false;
  63. protected bool initialValueCalculated = false;
  64. protected float springAngle;
  65. protected override void InitRequiredComponents()
  66. {
  67. initialLocalRotation = transform.localRotation;
  68. InitWheel();
  69. }
  70. protected override bool DetectSetup()
  71. {
  72. if (wheelHingeCreated)
  73. {
  74. wheelHinge.anchor = Vector3.up;
  75. wheelHinge.axis = Vector3.up;
  76. if (connectedTo)
  77. {
  78. wheelHinge.connectedBody = connectedTo.GetComponent<Rigidbody>();
  79. }
  80. }
  81. return true;
  82. }
  83. protected override ControlValueRange RegisterValueRange()
  84. {
  85. return new ControlValueRange()
  86. {
  87. controlMin = minimumValue,
  88. controlMax = maximumValue
  89. };
  90. }
  91. protected override void HandleUpdate()
  92. {
  93. CalculateValue();
  94. if (lockAtLimits && !initialValueCalculated)
  95. {
  96. transform.localRotation = initialLocalRotation;
  97. initialValueCalculated = true;
  98. }
  99. }
  100. protected virtual void InitWheel()
  101. {
  102. SetupRigidbody();
  103. SetupHinge();
  104. SetupInteractableObject();
  105. }
  106. protected virtual void SetupRigidbody()
  107. {
  108. wheelRigidbody = GetComponent<Rigidbody>();
  109. if (wheelRigidbody == null)
  110. {
  111. wheelRigidbody = gameObject.AddComponent<Rigidbody>();
  112. wheelRigidbody.angularDrag = releasedFriction;
  113. }
  114. wheelRigidbody.isKinematic = false;
  115. wheelRigidbody.useGravity = false;
  116. if (connectedTo)
  117. {
  118. Rigidbody connectedToRigidbody = connectedTo.GetComponent<Rigidbody>();
  119. if (connectedToRigidbody == null)
  120. {
  121. connectedToRigidbody = connectedTo.AddComponent<Rigidbody>();
  122. connectedToRigidbody.useGravity = false;
  123. connectedToRigidbody.isKinematic = true;
  124. }
  125. }
  126. }
  127. protected virtual void SetupHinge()
  128. {
  129. wheelHinge = GetComponent<HingeJoint>();
  130. if (wheelHinge == null)
  131. {
  132. wheelHinge = gameObject.AddComponent<HingeJoint>();
  133. wheelHingeCreated = true;
  134. }
  135. SetupHingeRestrictions();
  136. }
  137. protected virtual void SetupHingeRestrictions()
  138. {
  139. float minJointLimit = 0f;
  140. float maxJointLimit = maxAngle;
  141. float limitOffset = maxAngle - 180f;
  142. if (limitOffset > 0f)
  143. {
  144. minJointLimit -= limitOffset;
  145. maxJointLimit = 180f;
  146. }
  147. if (lockAtLimits)
  148. {
  149. wheelHinge.useLimits = true;
  150. JointLimits wheelLimits = new JointLimits();
  151. wheelLimits.min = minJointLimit;
  152. wheelLimits.max = maxJointLimit;
  153. wheelHinge.limits = wheelLimits;
  154. Vector3 adjustedLimitsAngle = transform.localEulerAngles;
  155. switch (Mathf.RoundToInt(initialLocalRotation.eulerAngles.z))
  156. {
  157. case 0:
  158. adjustedLimitsAngle = new Vector3(transform.localEulerAngles.x, transform.localEulerAngles.y - minJointLimit, transform.localEulerAngles.z);
  159. break;
  160. case 90:
  161. adjustedLimitsAngle = new Vector3(transform.localEulerAngles.x + minJointLimit, transform.localEulerAngles.y, transform.localEulerAngles.z);
  162. break;
  163. case 180:
  164. adjustedLimitsAngle = new Vector3(transform.localEulerAngles.x, transform.localEulerAngles.y + minJointLimit, transform.localEulerAngles.z);
  165. break;
  166. }
  167. transform.localEulerAngles = adjustedLimitsAngle;
  168. initialValueCalculated = false;
  169. }
  170. }
  171. protected virtual void ConfigureHingeSpring()
  172. {
  173. JointSpring snapSpring = new JointSpring();
  174. snapSpring.spring = springStrengthValue;
  175. snapSpring.damper = springDamperValue;
  176. snapSpring.targetPosition = springAngle + wheelHinge.limits.min;
  177. wheelHinge.spring = snapSpring;
  178. }
  179. protected virtual void SetupInteractableObject()
  180. {
  181. VRTK_InteractableObject wheelInteractableObject = GetComponent<VRTK_InteractableObject>();
  182. if (wheelInteractableObject == null)
  183. {
  184. wheelInteractableObject = gameObject.AddComponent<VRTK_InteractableObject>();
  185. }
  186. wheelInteractableObject.isGrabbable = true;
  187. VRTK_TrackObjectGrabAttach attachMechanic;
  188. if (grabType == GrabTypes.TrackObject)
  189. {
  190. attachMechanic = gameObject.AddComponent<VRTK_TrackObjectGrabAttach>();
  191. if (lockAtLimits)
  192. {
  193. attachMechanic.velocityLimit = 0f;
  194. attachMechanic.angularVelocityLimit = angularVelocityLimit;
  195. }
  196. }
  197. else
  198. {
  199. attachMechanic = gameObject.AddComponent<VRTK_RotatorTrackGrabAttach>();
  200. }
  201. attachMechanic.precisionGrab = true;
  202. attachMechanic.detachDistance = detatchDistance;
  203. wheelInteractableObject.grabAttachMechanicScript = attachMechanic;
  204. wheelInteractableObject.secondaryGrabActionScript = gameObject.AddComponent<SecondaryControllerGrabActions.VRTK_SwapControllerGrabAction>();
  205. wheelInteractableObject.stayGrabbedOnTeleport = false;
  206. wheelInteractableObject.InteractableObjectGrabbed += WheelInteractableObjectGrabbed;
  207. wheelInteractableObject.InteractableObjectUngrabbed += WheelInteractableObjectUngrabbed;
  208. }
  209. protected virtual void WheelInteractableObjectGrabbed(object sender, InteractableObjectEventArgs e)
  210. {
  211. wheelRigidbody.angularDrag = grabbedFriction;
  212. wheelHinge.useSpring = false;
  213. }
  214. protected virtual void WheelInteractableObjectUngrabbed(object sender, InteractableObjectEventArgs e)
  215. {
  216. wheelRigidbody.angularDrag = releasedFriction;
  217. if (snapToStep)
  218. {
  219. wheelHinge.useSpring = true;
  220. ConfigureHingeSpring();
  221. }
  222. }
  223. protected virtual void CalculateValue()
  224. {
  225. ControlValueRange controlValueRange = RegisterValueRange();
  226. float angle;
  227. Vector3 axis;
  228. Quaternion rotationDelta = transform.localRotation * Quaternion.Inverse(initialLocalRotation);
  229. rotationDelta.ToAngleAxis(out angle, out axis);
  230. float calculatedValue = Mathf.Round((controlValueRange.controlMin + Mathf.Clamp01(angle / maxAngle) * (controlValueRange.controlMax - controlValueRange.controlMin)) / stepSize) * stepSize;
  231. float flatValue = calculatedValue - controlValueRange.controlMin;
  232. float controlRange = controlValueRange.controlMax - controlValueRange.controlMin;
  233. springAngle = (flatValue / controlRange) * maxAngle;
  234. value = calculatedValue;
  235. }
  236. }
  237. }