ArrowHand.cs 8.2 KB

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