You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
12 KiB
C#

//======= 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();
}
}
}