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