ArrowHand.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: The object attached to the player's hand that spawns and fires the
  4. // arrow
  5. //
  6. //=============================================================================
  7. using UnityEngine;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. namespace Valve.VR.InteractionSystem
  11. {
  12. //-------------------------------------------------------------------------
  13. public class ArrowHand : MonoBehaviour
  14. {
  15. private Hand hand;
  16. private Longbow bow;
  17. private GameObject currentArrow;
  18. public GameObject arrowPrefab;
  19. public Transform arrowNockTransform;
  20. public float nockDistance = 0.1f;
  21. public float lerpCompleteDistance = 0.08f;
  22. public float rotationLerpThreshold = 0.15f;
  23. public float positionLerpThreshold = 0.15f;
  24. private bool allowArrowSpawn = true;
  25. private bool nocked;
  26. private bool inNockRange = false;
  27. private bool arrowLerpComplete = false;
  28. public SoundPlayOneshot arrowSpawnSound;
  29. private AllowTeleportWhileAttachedToHand allowTeleport = null;
  30. public int maxArrowCount = 10;
  31. private List<GameObject> arrowList;
  32. //-------------------------------------------------
  33. void Awake()
  34. {
  35. allowTeleport = GetComponent<AllowTeleportWhileAttachedToHand>();
  36. allowTeleport.teleportAllowed = true;
  37. allowTeleport.overrideHoverLock = false;
  38. arrowList = new List<GameObject>();
  39. }
  40. //-------------------------------------------------
  41. private void OnAttachedToHand( Hand attachedHand )
  42. {
  43. hand = attachedHand;
  44. FindBow();
  45. }
  46. //-------------------------------------------------
  47. private GameObject InstantiateArrow()
  48. {
  49. GameObject arrow = Instantiate( arrowPrefab, arrowNockTransform.position, arrowNockTransform.rotation ) as GameObject;
  50. arrow.name = "Bow Arrow";
  51. arrow.transform.parent = arrowNockTransform;
  52. Util.ResetTransform( arrow.transform );
  53. arrowList.Add( arrow );
  54. while ( arrowList.Count > maxArrowCount )
  55. {
  56. GameObject oldArrow = arrowList[0];
  57. arrowList.RemoveAt( 0 );
  58. if ( oldArrow )
  59. {
  60. Destroy( oldArrow );
  61. }
  62. }
  63. return arrow;
  64. }
  65. //-------------------------------------------------
  66. private void HandAttachedUpdate( Hand hand )
  67. {
  68. if ( bow == null )
  69. {
  70. FindBow();
  71. }
  72. if ( bow == null )
  73. {
  74. return;
  75. }
  76. if ( allowArrowSpawn && ( currentArrow == null ) ) // If we're allowed to have an active arrow in hand but don't yet, spawn one
  77. {
  78. currentArrow = InstantiateArrow();
  79. arrowSpawnSound.Play();
  80. }
  81. float distanceToNockPosition = Vector3.Distance( transform.parent.position, bow.nockTransform.position );
  82. // If there's an arrow spawned in the hand and it's not nocked yet
  83. if ( !nocked )
  84. {
  85. // If we're close enough to nock position that we want to start arrow rotation lerp, do so
  86. if ( distanceToNockPosition < rotationLerpThreshold )
  87. {
  88. float lerp = Util.RemapNumber( distanceToNockPosition, rotationLerpThreshold, lerpCompleteDistance, 0, 1 );
  89. arrowNockTransform.rotation = Quaternion.Lerp( arrowNockTransform.parent.rotation, bow.nockRestTransform.rotation, lerp );
  90. }
  91. else // Not close enough for rotation lerp, reset rotation
  92. {
  93. arrowNockTransform.localRotation = Quaternion.identity;
  94. }
  95. // If we're close enough to the nock position that we want to start arrow position lerp, do so
  96. if ( distanceToNockPosition < positionLerpThreshold )
  97. {
  98. float posLerp = Util.RemapNumber( distanceToNockPosition, positionLerpThreshold, lerpCompleteDistance, 0, 1 );
  99. posLerp = Mathf.Clamp( posLerp, 0f, 1f );
  100. arrowNockTransform.position = Vector3.Lerp( arrowNockTransform.parent.position, bow.nockRestTransform.position, posLerp );
  101. }
  102. else // Not close enough for position lerp, reset position
  103. {
  104. arrowNockTransform.position = arrowNockTransform.parent.position;
  105. }
  106. // Give a haptic tick when lerp is visually complete
  107. if ( distanceToNockPosition < lerpCompleteDistance )
  108. {
  109. if ( !arrowLerpComplete )
  110. {
  111. arrowLerpComplete = true;
  112. hand.controller.TriggerHapticPulse( 500 );
  113. }
  114. }
  115. else
  116. {
  117. if ( arrowLerpComplete )
  118. {
  119. arrowLerpComplete = false;
  120. }
  121. }
  122. // Allow nocking the arrow when controller is close enough
  123. if ( distanceToNockPosition < nockDistance )
  124. {
  125. if ( !inNockRange )
  126. {
  127. inNockRange = true;
  128. bow.ArrowInPosition();
  129. }
  130. }
  131. else
  132. {
  133. if ( inNockRange )
  134. {
  135. inNockRange = false;
  136. }
  137. }
  138. // If arrow is close enough to the nock position and we're pressing the trigger, and we're not nocked yet, Nock
  139. if ( ( distanceToNockPosition < nockDistance ) && hand.controller.GetPress( SteamVR_Controller.ButtonMask.Trigger ) && !nocked )
  140. {
  141. if ( currentArrow == null )
  142. {
  143. currentArrow = InstantiateArrow();
  144. }
  145. nocked = true;
  146. bow.StartNock( this );
  147. hand.HoverLock( GetComponent<Interactable>() );
  148. allowTeleport.teleportAllowed = false;
  149. currentArrow.transform.parent = bow.nockTransform;
  150. Util.ResetTransform( currentArrow.transform );
  151. Util.ResetTransform( arrowNockTransform );
  152. }
  153. }
  154. // If arrow is nocked, and we release the trigger
  155. if ( nocked && ( !hand.controller.GetPress( SteamVR_Controller.ButtonMask.Trigger ) || hand.controller.GetPressUp( SteamVR_Controller.ButtonMask.Trigger ) ) )
  156. {
  157. if ( bow.pulled ) // If bow is pulled back far enough, fire arrow, otherwise reset arrow in arrowhand
  158. {
  159. FireArrow();
  160. }
  161. else
  162. {
  163. arrowNockTransform.rotation = currentArrow.transform.rotation;
  164. currentArrow.transform.parent = arrowNockTransform;
  165. Util.ResetTransform( currentArrow.transform );
  166. nocked = false;
  167. bow.ReleaseNock();
  168. hand.HoverUnlock( GetComponent<Interactable>() );
  169. allowTeleport.teleportAllowed = true;
  170. }
  171. bow.StartRotationLerp(); // Arrow is releasing from the bow, tell the bow to lerp back to controller rotation
  172. }
  173. }
  174. //-------------------------------------------------
  175. private void OnDetachedFromHand( Hand hand )
  176. {
  177. Destroy( gameObject );
  178. }
  179. //-------------------------------------------------
  180. private void FireArrow()
  181. {
  182. currentArrow.transform.parent = null;
  183. Arrow arrow = currentArrow.GetComponent<Arrow>();
  184. arrow.shaftRB.isKinematic = false;
  185. arrow.shaftRB.useGravity = true;
  186. arrow.shaftRB.transform.GetComponent<BoxCollider>().enabled = true;
  187. arrow.arrowHeadRB.isKinematic = false;
  188. arrow.arrowHeadRB.useGravity = true;
  189. arrow.arrowHeadRB.transform.GetComponent<BoxCollider>().enabled = true;
  190. arrow.arrowHeadRB.AddForce( currentArrow.transform.forward * bow.GetArrowVelocity(), ForceMode.VelocityChange );
  191. arrow.arrowHeadRB.AddTorque( currentArrow.transform.forward * 10 );
  192. nocked = false;
  193. currentArrow.GetComponent<Arrow>().ArrowReleased( bow.GetArrowVelocity() );
  194. bow.ArrowReleased();
  195. allowArrowSpawn = false;
  196. Invoke( "EnableArrowSpawn", 0.5f );
  197. StartCoroutine( ArrowReleaseHaptics() );
  198. currentArrow = null;
  199. allowTeleport.teleportAllowed = true;
  200. }
  201. //-------------------------------------------------
  202. private void EnableArrowSpawn()
  203. {
  204. allowArrowSpawn = true;
  205. }
  206. //-------------------------------------------------
  207. private IEnumerator ArrowReleaseHaptics()
  208. {
  209. yield return new WaitForSeconds( 0.05f );
  210. hand.otherHand.controller.TriggerHapticPulse( 1500 );
  211. yield return new WaitForSeconds( 0.05f );
  212. hand.otherHand.controller.TriggerHapticPulse( 800 );
  213. yield return new WaitForSeconds( 0.05f );
  214. hand.otherHand.controller.TriggerHapticPulse( 500 );
  215. yield return new WaitForSeconds( 0.05f );
  216. hand.otherHand.controller.TriggerHapticPulse( 300 );
  217. }
  218. //-------------------------------------------------
  219. private void OnHandFocusLost( Hand hand )
  220. {
  221. gameObject.SetActive( false );
  222. }
  223. //-------------------------------------------------
  224. private void OnHandFocusAcquired( Hand hand )
  225. {
  226. gameObject.SetActive( true );
  227. }
  228. //-------------------------------------------------
  229. private void FindBow()
  230. {
  231. bow = hand.otherHand.GetComponentInChildren<Longbow>();
  232. }
  233. }
  234. }