Longbow.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: The bow
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. namespace Valve.VR.InteractionSystem
  10. {
  11. //-------------------------------------------------------------------------
  12. [RequireComponent( typeof( Interactable ) )]
  13. public class Longbow : MonoBehaviour
  14. {
  15. public enum Handedness { Left, Right };
  16. public Handedness currentHandGuess = Handedness.Left;
  17. private float timeOfPossibleHandSwitch = 0f;
  18. private float timeBeforeConfirmingHandSwitch = 1.5f;
  19. private bool possibleHandSwitch = false;
  20. public Transform pivotTransform;
  21. public Transform handleTransform;
  22. private Hand hand;
  23. private ArrowHand arrowHand;
  24. public Transform nockTransform;
  25. public Transform nockRestTransform;
  26. public bool autoSpawnArrowHand = true;
  27. public ItemPackage arrowHandItemPackage;
  28. public GameObject arrowHandPrefab;
  29. public bool nocked;
  30. public bool pulled;
  31. private const float minPull = 0.05f;
  32. private const float maxPull = 0.5f;
  33. private float nockDistanceTravelled = 0f;
  34. private float hapticDistanceThreshold = 0.01f;
  35. private float lastTickDistance;
  36. private const float bowPullPulseStrengthLow = 100;
  37. private const float bowPullPulseStrengthHigh = 500;
  38. private Vector3 bowLeftVector;
  39. public float arrowMinVelocity = 3f;
  40. public float arrowMaxVelocity = 30f;
  41. private float arrowVelocity = 30f;
  42. private float minStrainTickTime = 0.1f;
  43. private float maxStrainTickTime = 0.5f;
  44. private float nextStrainTick = 0;
  45. private bool lerpBackToZeroRotation;
  46. private float lerpStartTime;
  47. private float lerpDuration = 0.15f;
  48. private Quaternion lerpStartRotation;
  49. private float nockLerpStartTime;
  50. private Quaternion nockLerpStartRotation;
  51. public float drawOffset = 0.06f;
  52. public LinearMapping bowDrawLinearMapping;
  53. private Vector3 lateUpdatePos;
  54. private Quaternion lateUpdateRot;
  55. public SoundBowClick drawSound;
  56. private float drawTension;
  57. public SoundPlayOneshot arrowSlideSound;
  58. public SoundPlayOneshot releaseSound;
  59. public SoundPlayOneshot nockSound;
  60. SteamVR_Events.Action newPosesAppliedAction;
  61. //-------------------------------------------------
  62. private void OnAttachedToHand( Hand attachedHand )
  63. {
  64. hand = attachedHand;
  65. }
  66. //-------------------------------------------------
  67. private void HandAttachedUpdate( Hand hand )
  68. {
  69. // Reset transform since we cheated it right after getting poses on previous frame
  70. //transform.localPosition = Vector3.zero;
  71. //transform.localRotation = Quaternion.identity;
  72. // Update handedness guess
  73. EvaluateHandedness();
  74. if ( nocked )
  75. {
  76. Vector3 nockToarrowHand = ( arrowHand.arrowNockTransform.parent.position - nockRestTransform.position ); // Vector from bow nock transform to arrowhand nock transform - used to align bow when drawing
  77. // Align bow
  78. // Time lerp value used for ramping into drawn bow orientation
  79. float lerp = Util.RemapNumberClamped( Time.time, nockLerpStartTime, ( nockLerpStartTime + lerpDuration ), 0f, 1f );
  80. float pullLerp = Util.RemapNumberClamped( nockToarrowHand.magnitude, minPull, maxPull, 0f, 1f ); // Normalized current state of bow draw 0 - 1
  81. Vector3 arrowNockTransformToHeadset = ( ( Player.instance.hmdTransform.position + ( Vector3.down * 0.05f ) ) - arrowHand.arrowNockTransform.parent.position ).normalized;
  82. Vector3 arrowHandPosition = ( arrowHand.arrowNockTransform.parent.position + ( ( arrowNockTransformToHeadset * drawOffset ) * pullLerp ) ); // Use this line to lerp arrowHand nock position
  83. //Vector3 arrowHandPosition = arrowHand.arrowNockTransform.position; // Use this line if we don't want to lerp arrowHand nock position
  84. Vector3 pivotToString = ( arrowHandPosition - pivotTransform.position ).normalized;
  85. Vector3 pivotToLowerHandle = ( handleTransform.position - pivotTransform.position ).normalized;
  86. bowLeftVector = -Vector3.Cross( pivotToLowerHandle, pivotToString );
  87. pivotTransform.rotation = Quaternion.Lerp( nockLerpStartRotation, Quaternion.LookRotation( pivotToString, bowLeftVector ), lerp );
  88. // Move nock position
  89. if ( Vector3.Dot( nockToarrowHand, -nockTransform.forward ) > 0 )
  90. {
  91. float distanceToarrowHand = nockToarrowHand.magnitude * lerp;
  92. nockTransform.localPosition = new Vector3( 0f, 0f, Mathf.Clamp( -distanceToarrowHand, -maxPull, 0f ) );
  93. nockDistanceTravelled = -nockTransform.localPosition.z;
  94. arrowVelocity = Util.RemapNumber( nockDistanceTravelled, minPull, maxPull, arrowMinVelocity, arrowMaxVelocity );
  95. drawTension = Util.RemapNumberClamped( nockDistanceTravelled, 0, maxPull, 0f, 1f );
  96. this.bowDrawLinearMapping.value = drawTension; // Send drawTension value to LinearMapping script, which drives the bow draw animation
  97. if ( nockDistanceTravelled > minPull )
  98. {
  99. pulled = true;
  100. }
  101. else
  102. {
  103. pulled = false;
  104. }
  105. if ( ( nockDistanceTravelled > ( lastTickDistance + hapticDistanceThreshold ) ) || nockDistanceTravelled < ( lastTickDistance - hapticDistanceThreshold ) )
  106. {
  107. ushort hapticStrength = (ushort)Util.RemapNumber( nockDistanceTravelled, 0, maxPull, bowPullPulseStrengthLow, bowPullPulseStrengthHigh );
  108. hand.TriggerHapticPulse( hapticStrength );
  109. hand.otherHand.TriggerHapticPulse( hapticStrength );
  110. drawSound.PlayBowTensionClicks( drawTension );
  111. lastTickDistance = nockDistanceTravelled;
  112. }
  113. if ( nockDistanceTravelled >= maxPull )
  114. {
  115. if ( Time.time > nextStrainTick )
  116. {
  117. hand.TriggerHapticPulse( 400 );
  118. hand.otherHand.TriggerHapticPulse( 400 );
  119. drawSound.PlayBowTensionClicks( drawTension );
  120. nextStrainTick = Time.time + Random.Range( minStrainTickTime, maxStrainTickTime );
  121. }
  122. }
  123. }
  124. else
  125. {
  126. nockTransform.localPosition = new Vector3( 0f, 0f, 0f );
  127. this.bowDrawLinearMapping.value = 0f;
  128. }
  129. }
  130. else
  131. {
  132. if ( lerpBackToZeroRotation )
  133. {
  134. float lerp = Util.RemapNumber( Time.time, lerpStartTime, lerpStartTime + lerpDuration, 0, 1 );
  135. pivotTransform.localRotation = Quaternion.Lerp( lerpStartRotation, Quaternion.identity, lerp );
  136. if ( lerp >= 1 )
  137. {
  138. lerpBackToZeroRotation = false;
  139. }
  140. }
  141. }
  142. }
  143. //-------------------------------------------------
  144. public void ArrowReleased()
  145. {
  146. nocked = false;
  147. hand.HoverUnlock( GetComponent<Interactable>() );
  148. hand.otherHand.HoverUnlock( arrowHand.GetComponent<Interactable>() );
  149. if ( releaseSound != null )
  150. {
  151. releaseSound.Play();
  152. }
  153. this.StartCoroutine( this.ResetDrawAnim() );
  154. }
  155. //-------------------------------------------------
  156. private IEnumerator ResetDrawAnim()
  157. {
  158. float startTime = Time.time;
  159. float startLerp = drawTension;
  160. while ( Time.time < ( startTime + 0.02f ) )
  161. {
  162. float lerp = Util.RemapNumberClamped( Time.time, startTime, startTime + 0.02f, startLerp, 0f );
  163. this.bowDrawLinearMapping.value = lerp;
  164. yield return null;
  165. }
  166. this.bowDrawLinearMapping.value = 0;
  167. yield break;
  168. }
  169. //-------------------------------------------------
  170. public float GetArrowVelocity()
  171. {
  172. return arrowVelocity;
  173. }
  174. //-------------------------------------------------
  175. public void StartRotationLerp()
  176. {
  177. lerpStartTime = Time.time;
  178. lerpBackToZeroRotation = true;
  179. lerpStartRotation = pivotTransform.localRotation;
  180. Util.ResetTransform( nockTransform );
  181. }
  182. //-------------------------------------------------
  183. public void StartNock( ArrowHand currentArrowHand )
  184. {
  185. arrowHand = currentArrowHand;
  186. hand.HoverLock( GetComponent<Interactable>() );
  187. nocked = true;
  188. nockLerpStartTime = Time.time;
  189. nockLerpStartRotation = pivotTransform.rotation;
  190. // Sound of arrow sliding on nock as it's being pulled back
  191. arrowSlideSound.Play();
  192. // Decide which hand we're drawing with and lerp to the correct side
  193. DoHandednessCheck();
  194. }
  195. //-------------------------------------------------
  196. private void EvaluateHandedness()
  197. {
  198. var handType = hand.handType;
  199. if ( handType == SteamVR_Input_Sources.LeftHand )// Bow hand is further left than arrow hand.
  200. {
  201. // We were considering a switch, but the current controller orientation matches our currently assigned handedness, so no longer consider a switch
  202. if ( possibleHandSwitch && currentHandGuess == Handedness.Left )
  203. {
  204. possibleHandSwitch = false;
  205. }
  206. // If we previously thought the bow was right-handed, and were not already considering switching, start considering a switch
  207. if ( !possibleHandSwitch && currentHandGuess == Handedness.Right )
  208. {
  209. possibleHandSwitch = true;
  210. timeOfPossibleHandSwitch = Time.time;
  211. }
  212. // If we are considering a handedness switch, and it's been this way long enough, switch
  213. if ( possibleHandSwitch && Time.time > ( timeOfPossibleHandSwitch + timeBeforeConfirmingHandSwitch ) )
  214. {
  215. currentHandGuess = Handedness.Left;
  216. possibleHandSwitch = false;
  217. }
  218. }
  219. else // Bow hand is further right than arrow hand
  220. {
  221. // We were considering a switch, but the current controller orientation matches our currently assigned handedness, so no longer consider a switch
  222. if ( possibleHandSwitch && currentHandGuess == Handedness.Right )
  223. {
  224. possibleHandSwitch = false;
  225. }
  226. // If we previously thought the bow was right-handed, and were not already considering switching, start considering a switch
  227. if ( !possibleHandSwitch && currentHandGuess == Handedness.Left )
  228. {
  229. possibleHandSwitch = true;
  230. timeOfPossibleHandSwitch = Time.time;
  231. }
  232. // If we are considering a handedness switch, and it's been this way long enough, switch
  233. if ( possibleHandSwitch && Time.time > ( timeOfPossibleHandSwitch + timeBeforeConfirmingHandSwitch ) )
  234. {
  235. currentHandGuess = Handedness.Right;
  236. possibleHandSwitch = false;
  237. }
  238. }
  239. }
  240. //-------------------------------------------------
  241. private void DoHandednessCheck()
  242. {
  243. // Based on our current best guess about hand, switch bow orientation and arrow lerp direction
  244. if ( currentHandGuess == Handedness.Left )
  245. {
  246. pivotTransform.localScale = new Vector3( 1f, 1f, 1f );
  247. }
  248. else
  249. {
  250. pivotTransform.localScale = new Vector3( 1f, -1f, 1f );
  251. }
  252. }
  253. //-------------------------------------------------
  254. public void ArrowInPosition()
  255. {
  256. DoHandednessCheck();
  257. if ( nockSound != null )
  258. {
  259. nockSound.Play();
  260. }
  261. }
  262. //-------------------------------------------------
  263. public void ReleaseNock()
  264. {
  265. // ArrowHand tells us to do this when we release the buttons when bow is nocked but not drawn far enough
  266. nocked = false;
  267. hand.HoverUnlock( GetComponent<Interactable>() );
  268. this.StartCoroutine( this.ResetDrawAnim() );
  269. }
  270. //-------------------------------------------------
  271. private void ShutDown()
  272. {
  273. if ( hand != null && hand.otherHand.currentAttachedObject != null )
  274. {
  275. if ( hand.otherHand.currentAttachedObject.GetComponent<ItemPackageReference>() != null )
  276. {
  277. if ( hand.otherHand.currentAttachedObject.GetComponent<ItemPackageReference>().itemPackage == arrowHandItemPackage )
  278. {
  279. hand.otherHand.DetachObject( hand.otherHand.currentAttachedObject );
  280. }
  281. }
  282. }
  283. }
  284. //-------------------------------------------------
  285. private void OnHandFocusLost( Hand hand )
  286. {
  287. gameObject.SetActive( false );
  288. }
  289. //-------------------------------------------------
  290. private void OnHandFocusAcquired( Hand hand )
  291. {
  292. gameObject.SetActive( true );
  293. OnAttachedToHand( hand );
  294. }
  295. //-------------------------------------------------
  296. private void OnDetachedFromHand( Hand hand )
  297. {
  298. Destroy( gameObject );
  299. }
  300. //-------------------------------------------------
  301. void OnDestroy()
  302. {
  303. ShutDown();
  304. }
  305. }
  306. }