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