Longbow.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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 bool deferNewPoses = false;
  54. private Vector3 lateUpdatePos;
  55. private Quaternion lateUpdateRot;
  56. public SoundBowClick drawSound;
  57. private float drawTension;
  58. public SoundPlayOneshot arrowSlideSound;
  59. public SoundPlayOneshot releaseSound;
  60. public SoundPlayOneshot nockSound;
  61. SteamVR_Events.Action newPosesAppliedAction;
  62. //-------------------------------------------------
  63. private void OnAttachedToHand( Hand attachedHand )
  64. {
  65. hand = attachedHand;
  66. }
  67. //-------------------------------------------------
  68. void Awake()
  69. {
  70. newPosesAppliedAction = SteamVR_Events.NewPosesAppliedAction( OnNewPosesApplied );
  71. }
  72. //-------------------------------------------------
  73. void OnEnable()
  74. {
  75. newPosesAppliedAction.enabled = true;
  76. }
  77. //-------------------------------------------------
  78. void OnDisable()
  79. {
  80. newPosesAppliedAction.enabled = false;
  81. }
  82. //-------------------------------------------------
  83. void LateUpdate()
  84. {
  85. if ( deferNewPoses )
  86. {
  87. lateUpdatePos = transform.position;
  88. lateUpdateRot = transform.rotation;
  89. }
  90. }
  91. //-------------------------------------------------
  92. private void OnNewPosesApplied()
  93. {
  94. if ( deferNewPoses )
  95. {
  96. // Set longbow object back to previous pose position to avoid jitter
  97. transform.position = lateUpdatePos;
  98. transform.rotation = lateUpdateRot;
  99. deferNewPoses = false;
  100. }
  101. }
  102. //-------------------------------------------------
  103. private void HandAttachedUpdate( Hand hand )
  104. {
  105. // Reset transform since we cheated it right after getting poses on previous frame
  106. transform.localPosition = Vector3.zero;
  107. transform.localRotation = Quaternion.identity;
  108. // Update handedness guess
  109. EvaluateHandedness();
  110. if ( nocked )
  111. {
  112. deferNewPoses = true;
  113. Vector3 nockToarrowHand = ( arrowHand.arrowNockTransform.parent.position - nockRestTransform.position ); // Vector from bow nock transform to arrowhand nock transform - used to align bow when drawing
  114. // Align bow
  115. // Time lerp value used for ramping into drawn bow orientation
  116. float lerp = Util.RemapNumberClamped( Time.time, nockLerpStartTime, ( nockLerpStartTime + lerpDuration ), 0f, 1f );
  117. float pullLerp = Util.RemapNumberClamped( nockToarrowHand.magnitude, minPull, maxPull, 0f, 1f ); // Normalized current state of bow draw 0 - 1
  118. Vector3 arrowNockTransformToHeadset = ( ( Player.instance.hmdTransform.position + ( Vector3.down * 0.05f ) ) - arrowHand.arrowNockTransform.parent.position ).normalized;
  119. Vector3 arrowHandPosition = ( arrowHand.arrowNockTransform.parent.position + ( ( arrowNockTransformToHeadset * drawOffset ) * pullLerp ) ); // Use this line to lerp arrowHand nock position
  120. //Vector3 arrowHandPosition = arrowHand.arrowNockTransform.position; // Use this line if we don't want to lerp arrowHand nock position
  121. Vector3 pivotToString = ( arrowHandPosition - pivotTransform.position ).normalized;
  122. Vector3 pivotToLowerHandle = ( handleTransform.position - pivotTransform.position ).normalized;
  123. bowLeftVector = -Vector3.Cross( pivotToLowerHandle, pivotToString );
  124. pivotTransform.rotation = Quaternion.Lerp( nockLerpStartRotation, Quaternion.LookRotation( pivotToString, bowLeftVector ), lerp );
  125. // Move nock position
  126. if ( Vector3.Dot( nockToarrowHand, -nockTransform.forward ) > 0 )
  127. {
  128. float distanceToarrowHand = nockToarrowHand.magnitude * lerp;
  129. nockTransform.localPosition = new Vector3( 0f, 0f, Mathf.Clamp( -distanceToarrowHand, -maxPull, 0f ) );
  130. nockDistanceTravelled = -nockTransform.localPosition.z;
  131. arrowVelocity = Util.RemapNumber( nockDistanceTravelled, minPull, maxPull, arrowMinVelocity, arrowMaxVelocity );
  132. drawTension = Util.RemapNumberClamped( nockDistanceTravelled, 0, maxPull, 0f, 1f );
  133. this.bowDrawLinearMapping.value = drawTension; // Send drawTension value to LinearMapping script, which drives the bow draw animation
  134. if ( nockDistanceTravelled > minPull )
  135. {
  136. pulled = true;
  137. }
  138. else
  139. {
  140. pulled = false;
  141. }
  142. if ( ( nockDistanceTravelled > ( lastTickDistance + hapticDistanceThreshold ) ) || nockDistanceTravelled < ( lastTickDistance - hapticDistanceThreshold ) )
  143. {
  144. ushort hapticStrength = (ushort)Util.RemapNumber( nockDistanceTravelled, 0, maxPull, bowPullPulseStrengthLow, bowPullPulseStrengthHigh );
  145. hand.controller.TriggerHapticPulse( hapticStrength );
  146. hand.otherHand.controller.TriggerHapticPulse( hapticStrength );
  147. drawSound.PlayBowTensionClicks( drawTension );
  148. lastTickDistance = nockDistanceTravelled;
  149. }
  150. if ( nockDistanceTravelled >= maxPull )
  151. {
  152. if ( Time.time > nextStrainTick )
  153. {
  154. hand.controller.TriggerHapticPulse( 400 );
  155. hand.otherHand.controller.TriggerHapticPulse( 400 );
  156. drawSound.PlayBowTensionClicks( drawTension );
  157. nextStrainTick = Time.time + Random.Range( minStrainTickTime, maxStrainTickTime );
  158. }
  159. }
  160. }
  161. else
  162. {
  163. nockTransform.localPosition = new Vector3( 0f, 0f, 0f );
  164. this.bowDrawLinearMapping.value = 0f;
  165. }
  166. }
  167. else
  168. {
  169. if ( lerpBackToZeroRotation )
  170. {
  171. float lerp = Util.RemapNumber( Time.time, lerpStartTime, lerpStartTime + lerpDuration, 0, 1 );
  172. pivotTransform.localRotation = Quaternion.Lerp( lerpStartRotation, Quaternion.identity, lerp );
  173. if ( lerp >= 1 )
  174. {
  175. lerpBackToZeroRotation = false;
  176. }
  177. }
  178. }
  179. }
  180. //-------------------------------------------------
  181. public void ArrowReleased()
  182. {
  183. nocked = false;
  184. hand.HoverUnlock( GetComponent<Interactable>() );
  185. hand.otherHand.HoverUnlock( arrowHand.GetComponent<Interactable>() );
  186. if ( releaseSound != null )
  187. {
  188. releaseSound.Play();
  189. }
  190. this.StartCoroutine( this.ResetDrawAnim() );
  191. }
  192. //-------------------------------------------------
  193. private IEnumerator ResetDrawAnim()
  194. {
  195. float startTime = Time.time;
  196. float startLerp = drawTension;
  197. while ( Time.time < ( startTime + 0.02f ) )
  198. {
  199. float lerp = Util.RemapNumberClamped( Time.time, startTime, startTime + 0.02f, startLerp, 0f );
  200. this.bowDrawLinearMapping.value = lerp;
  201. yield return null;
  202. }
  203. this.bowDrawLinearMapping.value = 0;
  204. yield break;
  205. }
  206. //-------------------------------------------------
  207. public float GetArrowVelocity()
  208. {
  209. return arrowVelocity;
  210. }
  211. //-------------------------------------------------
  212. public void StartRotationLerp()
  213. {
  214. lerpStartTime = Time.time;
  215. lerpBackToZeroRotation = true;
  216. lerpStartRotation = pivotTransform.localRotation;
  217. Util.ResetTransform( nockTransform );
  218. }
  219. //-------------------------------------------------
  220. public void StartNock( ArrowHand currentArrowHand )
  221. {
  222. arrowHand = currentArrowHand;
  223. hand.HoverLock( GetComponent<Interactable>() );
  224. nocked = true;
  225. nockLerpStartTime = Time.time;
  226. nockLerpStartRotation = pivotTransform.rotation;
  227. // Sound of arrow sliding on nock as it's being pulled back
  228. arrowSlideSound.Play();
  229. // Decide which hand we're drawing with and lerp to the correct side
  230. DoHandednessCheck();
  231. }
  232. //-------------------------------------------------
  233. private void EvaluateHandedness()
  234. {
  235. Hand.HandType handType = hand.GuessCurrentHandType();
  236. if ( handType == Hand.HandType.Left )// Bow hand is further left than arrow hand.
  237. {
  238. // We were considering a switch, but the current controller orientation matches our currently assigned handedness, so no longer consider a switch
  239. if ( possibleHandSwitch && currentHandGuess == Handedness.Left )
  240. {
  241. possibleHandSwitch = false;
  242. }
  243. // If we previously thought the bow was right-handed, and were not already considering switching, start considering a switch
  244. if ( !possibleHandSwitch && currentHandGuess == Handedness.Right )
  245. {
  246. possibleHandSwitch = true;
  247. timeOfPossibleHandSwitch = Time.time;
  248. }
  249. // If we are considering a handedness switch, and it's been this way long enough, switch
  250. if ( possibleHandSwitch && Time.time > ( timeOfPossibleHandSwitch + timeBeforeConfirmingHandSwitch ) )
  251. {
  252. currentHandGuess = Handedness.Left;
  253. possibleHandSwitch = false;
  254. }
  255. }
  256. else // Bow hand is further right than arrow hand
  257. {
  258. // We were considering a switch, but the current controller orientation matches our currently assigned handedness, so no longer consider a switch
  259. if ( possibleHandSwitch && currentHandGuess == Handedness.Right )
  260. {
  261. possibleHandSwitch = false;
  262. }
  263. // If we previously thought the bow was right-handed, and were not already considering switching, start considering a switch
  264. if ( !possibleHandSwitch && currentHandGuess == Handedness.Left )
  265. {
  266. possibleHandSwitch = true;
  267. timeOfPossibleHandSwitch = Time.time;
  268. }
  269. // If we are considering a handedness switch, and it's been this way long enough, switch
  270. if ( possibleHandSwitch && Time.time > ( timeOfPossibleHandSwitch + timeBeforeConfirmingHandSwitch ) )
  271. {
  272. currentHandGuess = Handedness.Right;
  273. possibleHandSwitch = false;
  274. }
  275. }
  276. }
  277. //-------------------------------------------------
  278. private void DoHandednessCheck()
  279. {
  280. // Based on our current best guess about hand, switch bow orientation and arrow lerp direction
  281. if ( currentHandGuess == Handedness.Left )
  282. {
  283. pivotTransform.localScale = new Vector3( 1f, 1f, 1f );
  284. }
  285. else
  286. {
  287. pivotTransform.localScale = new Vector3( 1f, -1f, 1f );
  288. }
  289. }
  290. //-------------------------------------------------
  291. public void ArrowInPosition()
  292. {
  293. DoHandednessCheck();
  294. if ( nockSound != null )
  295. {
  296. nockSound.Play();
  297. }
  298. }
  299. //-------------------------------------------------
  300. public void ReleaseNock()
  301. {
  302. // ArrowHand tells us to do this when we release the buttons when bow is nocked but not drawn far enough
  303. nocked = false;
  304. hand.HoverUnlock( GetComponent<Interactable>() );
  305. this.StartCoroutine( this.ResetDrawAnim() );
  306. }
  307. //-------------------------------------------------
  308. private void ShutDown()
  309. {
  310. if ( hand != null && hand.otherHand.currentAttachedObject != null )
  311. {
  312. if ( hand.otherHand.currentAttachedObject.GetComponent<ItemPackageReference>() != null )
  313. {
  314. if ( hand.otherHand.currentAttachedObject.GetComponent<ItemPackageReference>().itemPackage == arrowHandItemPackage )
  315. {
  316. hand.otherHand.DetachObject( hand.otherHand.currentAttachedObject );
  317. }
  318. }
  319. }
  320. }
  321. //-------------------------------------------------
  322. private void OnHandFocusLost( Hand hand )
  323. {
  324. gameObject.SetActive( false );
  325. }
  326. //-------------------------------------------------
  327. private void OnHandFocusAcquired( Hand hand )
  328. {
  329. gameObject.SetActive( true );
  330. OnAttachedToHand( hand );
  331. }
  332. //-------------------------------------------------
  333. private void OnDetachedFromHand( Hand hand )
  334. {
  335. Destroy( gameObject );
  336. }
  337. //-------------------------------------------------
  338. void OnDestroy()
  339. {
  340. ShutDown();
  341. }
  342. }
  343. }