IMHOTEP Framework
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Pages
Throwable.cs
1 //======= Copyright (c) Valve Corporation, All rights reserved. ===============
2 //
3 // Purpose: Basic throwable object
4 //
5 //=============================================================================
6 
7 using UnityEngine;
8 using UnityEngine.Events;
9 using System.Collections;
10 
11 namespace Valve.VR.InteractionSystem
12 {
13  //-------------------------------------------------------------------------
14  [RequireComponent( typeof( Interactable ) )]
15  [RequireComponent( typeof( Rigidbody ) )]
16  [RequireComponent( typeof( VelocityEstimator ) )]
17  public class Throwable : MonoBehaviour
18  {
19  [EnumFlags]
20  [Tooltip( "The flags used to attach this object to the hand." )]
21  public Hand.AttachmentFlags attachmentFlags = Hand.AttachmentFlags.ParentToHand | Hand.AttachmentFlags.DetachFromOtherHand;
22 
23  [Tooltip( "Name of the attachment transform under in the hand's hierarchy which the object should should snap to." )]
24  public string attachmentPoint;
25 
26  [Tooltip( "How fast must this object be moving to attach due to a trigger hold instead of a trigger press?" )]
27  public float catchSpeedThreshold = 0.0f;
28 
29  [Tooltip( "When detaching the object, should it return to its original parent?" )]
30  public bool restoreOriginalParent = false;
31 
32  public bool attachEaseIn = false;
33  public AnimationCurve snapAttachEaseInCurve = AnimationCurve.EaseInOut( 0.0f, 0.0f, 1.0f, 1.0f );
34  public float snapAttachEaseInTime = 0.15f;
35  public string[] attachEaseInAttachmentNames;
36 
37  private VelocityEstimator velocityEstimator;
38  private bool attached = false;
39  private float attachTime;
40  private Vector3 attachPosition;
41  private Quaternion attachRotation;
42  private Transform attachEaseInTransform;
43 
44  public UnityEvent onPickUp;
45  public UnityEvent onDetachFromHand;
46 
47  public bool snapAttachEaseInCompleted = false;
48 
49 
50  //-------------------------------------------------
51  void Awake()
52  {
53  velocityEstimator = GetComponent<VelocityEstimator>();
54 
55  if ( attachEaseIn )
56  {
57  attachmentFlags &= ~Hand.AttachmentFlags.SnapOnAttach;
58  }
59 
60  Rigidbody rb = GetComponent<Rigidbody>();
61  rb.maxAngularVelocity = 50.0f;
62  }
63 
64 
65  //-------------------------------------------------
66  private void OnHandHoverBegin( Hand hand )
67  {
68  bool showHint = false;
69 
70  // "Catch" the throwable by holding down the interaction button instead of pressing it.
71  // Only do this if the throwable is moving faster than the prescribed threshold speed,
72  // and if it isn't attached to another hand
73  if ( !attached )
74  {
75  if ( hand.GetStandardInteractionButton() )
76  {
77  Rigidbody rb = GetComponent<Rigidbody>();
78  if ( rb.velocity.magnitude >= catchSpeedThreshold )
79  {
80  hand.AttachObject( gameObject, attachmentFlags, attachmentPoint );
81  showHint = false;
82  }
83  }
84  }
85 
86  if ( showHint )
87  {
88  ControllerButtonHints.ShowButtonHint( hand, Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger );
89  }
90  }
91 
92 
93  //-------------------------------------------------
94  private void OnHandHoverEnd( Hand hand )
95  {
96  ControllerButtonHints.HideButtonHint( hand, Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger );
97  }
98 
99 
100  //-------------------------------------------------
101  private void HandHoverUpdate( Hand hand )
102  {
103  //Trigger got pressed
104  if ( hand.GetStandardInteractionButtonDown() )
105  {
106  hand.AttachObject( gameObject, attachmentFlags, attachmentPoint );
107  ControllerButtonHints.HideButtonHint( hand, Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger );
108  }
109  }
110 
111  //-------------------------------------------------
112  private void OnAttachedToHand( Hand hand )
113  {
114  attached = true;
115 
116  onPickUp.Invoke();
117 
118  hand.HoverLock( null );
119 
120  Rigidbody rb = GetComponent<Rigidbody>();
121  rb.isKinematic = true;
122  rb.interpolation = RigidbodyInterpolation.None;
123 
124  if ( hand.controller == null )
125  {
126  velocityEstimator.BeginEstimatingVelocity();
127  }
128 
129  attachTime = Time.time;
130  attachPosition = transform.position;
131  attachRotation = transform.rotation;
132 
133  if ( attachEaseIn )
134  {
135  attachEaseInTransform = hand.transform;
136  if ( !Util.IsNullOrEmpty( attachEaseInAttachmentNames ) )
137  {
138  float smallestAngle = float.MaxValue;
139  for ( int i = 0; i < attachEaseInAttachmentNames.Length; i++ )
140  {
141  Transform t = hand.GetAttachmentTransform( attachEaseInAttachmentNames[i] );
142  float angle = Quaternion.Angle( t.rotation, attachRotation );
143  if ( angle < smallestAngle )
144  {
145  attachEaseInTransform = t;
146  smallestAngle = angle;
147  }
148  }
149  }
150  }
151 
152  snapAttachEaseInCompleted = false;
153  }
154 
155 
156  //-------------------------------------------------
157  private void OnDetachedFromHand( Hand hand )
158  {
159  attached = false;
160 
161  onDetachFromHand.Invoke();
162 
163  hand.HoverUnlock( null );
164 
165  Rigidbody rb = GetComponent<Rigidbody>();
166  rb.isKinematic = false;
167  rb.interpolation = RigidbodyInterpolation.Interpolate;
168 
169  Vector3 position = Vector3.zero;
170  Vector3 velocity = Vector3.zero;
171  Vector3 angularVelocity = Vector3.zero;
172  if ( hand.controller == null )
173  {
174  velocityEstimator.FinishEstimatingVelocity();
175  velocity = velocityEstimator.GetVelocityEstimate();
176  angularVelocity = velocityEstimator.GetAngularVelocityEstimate();
177  position = velocityEstimator.transform.position;
178  }
179  else
180  {
181  velocity = Player.instance.trackingOriginTransform.TransformVector( hand.controller.velocity );
182  angularVelocity = Player.instance.trackingOriginTransform.TransformVector( hand.controller.angularVelocity );
183  position = hand.transform.position;
184  }
185 
186  Vector3 r = transform.TransformPoint( rb.centerOfMass ) - position;
187  rb.velocity = velocity + Vector3.Cross( angularVelocity, r );
188  rb.angularVelocity = angularVelocity;
189 
190  // Make the object travel at the release velocity for the amount
191  // of time it will take until the next fixed update, at which
192  // point Unity physics will take over
193  float timeUntilFixedUpdate = ( Time.fixedDeltaTime + Time.fixedTime ) - Time.time;
194  transform.position += timeUntilFixedUpdate * velocity;
195  float angle = Mathf.Rad2Deg * angularVelocity.magnitude;
196  Vector3 axis = angularVelocity.normalized;
197  transform.rotation *= Quaternion.AngleAxis( angle * timeUntilFixedUpdate, axis );
198  }
199 
200 
201  //-------------------------------------------------
202  private void HandAttachedUpdate( Hand hand )
203  {
204  //Trigger got released
205  if ( !hand.GetStandardInteractionButton() )
206  {
207  // Detach ourselves late in the frame.
208  // This is so that any vehicles the player is attached to
209  // have a chance to finish updating themselves.
210  // If we detach now, our position could be behind what it
211  // will be at the end of the frame, and the object may appear
212  // to teleport behind the hand when the player releases it.
213  StartCoroutine( LateDetach( hand ) );
214  }
215 
216  if ( attachEaseIn )
217  {
218  float t = Util.RemapNumberClamped( Time.time, attachTime, attachTime + snapAttachEaseInTime, 0.0f, 1.0f );
219  if ( t < 1.0f )
220  {
221  t = snapAttachEaseInCurve.Evaluate( t );
222  transform.position = Vector3.Lerp( attachPosition, attachEaseInTransform.position, t );
223  transform.rotation = Quaternion.Lerp( attachRotation, attachEaseInTransform.rotation, t );
224  }
225  else if ( !snapAttachEaseInCompleted )
226  {
227  gameObject.SendMessage( "OnThrowableAttachEaseInCompleted", hand, SendMessageOptions.DontRequireReceiver );
228  snapAttachEaseInCompleted = true;
229  }
230  }
231  }
232 
233 
234  //-------------------------------------------------
235  private IEnumerator LateDetach( Hand hand )
236  {
237  yield return new WaitForEndOfFrame();
238 
239  hand.DetachObject( gameObject, restoreOriginalParent );
240  }
241 
242 
243  //-------------------------------------------------
244  private void OnHandFocusAcquired( Hand hand )
245  {
246  gameObject.SetActive( true );
247  velocityEstimator.BeginEstimatingVelocity();
248  }
249 
250 
251  //-------------------------------------------------
252  private void OnHandFocusLost( Hand hand )
253  {
254  gameObject.SetActive( false );
255  velocityEstimator.FinishEstimatingVelocity();
256  }
257  }
258 }