123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- //======= 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 bool deferNewPoses = false;
- 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;
- }
- //-------------------------------------------------
- void Awake()
- {
- newPosesAppliedAction = SteamVR_Events.NewPosesAppliedAction( OnNewPosesApplied );
- }
- //-------------------------------------------------
- void OnEnable()
- {
- newPosesAppliedAction.enabled = true;
- }
- //-------------------------------------------------
- void OnDisable()
- {
- newPosesAppliedAction.enabled = false;
- }
- //-------------------------------------------------
- void LateUpdate()
- {
- if ( deferNewPoses )
- {
- lateUpdatePos = transform.position;
- lateUpdateRot = transform.rotation;
- }
- }
- //-------------------------------------------------
- private void OnNewPosesApplied()
- {
- if ( deferNewPoses )
- {
- // Set longbow object back to previous pose position to avoid jitter
- transform.position = lateUpdatePos;
- transform.rotation = lateUpdateRot;
- deferNewPoses = false;
- }
- }
- //-------------------------------------------------
- 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 )
- {
- deferNewPoses = true;
- 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.controller.TriggerHapticPulse( hapticStrength );
- hand.otherHand.controller.TriggerHapticPulse( hapticStrength );
- drawSound.PlayBowTensionClicks( drawTension );
- lastTickDistance = nockDistanceTravelled;
- }
- if ( nockDistanceTravelled >= maxPull )
- {
- if ( Time.time > nextStrainTick )
- {
- hand.controller.TriggerHapticPulse( 400 );
- hand.otherHand.controller.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()
- {
- Hand.HandType handType = hand.GuessCurrentHandType();
- if ( handType == Hand.HandType.Left )// 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();
- }
- }
- }
|