IMHOTEP Framework
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Pages
Teleport.cs
1 //======= Copyright (c) Valve Corporation, All rights reserved. ===============
2 //
3 // Purpose: Handles all the teleport logic
4 //
5 //=============================================================================
6 
7 using UnityEngine;
8 using UnityEngine.Events;
9 using System.Collections;
10 
11 namespace Valve.VR.InteractionSystem
12 {
13  //-------------------------------------------------------------------------
14  public class Teleport : MonoBehaviour
15  {
16  public LayerMask traceLayerMask;
17  public LayerMask floorFixupTraceLayerMask;
18  public float floorFixupMaximumTraceDistance = 1.0f;
19  public Material areaVisibleMaterial;
20  public Material areaLockedMaterial;
21  public Material areaHighlightedMaterial;
22  public Material pointVisibleMaterial;
23  public Material pointLockedMaterial;
24  public Material pointHighlightedMaterial;
25  public Transform destinationReticleTransform;
26  public Transform invalidReticleTransform;
27  public GameObject playAreaPreviewCorner;
28  public GameObject playAreaPreviewSide;
29  public Color pointerValidColor;
30  public Color pointerInvalidColor;
31  public Color pointerLockedColor;
32  public bool showPlayAreaMarker = true;
33 
34  public float teleportFadeTime = 0.1f;
35  public float meshFadeTime = 0.2f;
36 
37  public float arcDistance = 10.0f;
38 
39  [Header( "Effects" )]
40  public Transform onActivateObjectTransform;
41  public Transform onDeactivateObjectTransform;
42  public float activateObjectTime = 1.0f;
43  public float deactivateObjectTime = 1.0f;
44 
45  [Header( "Audio Sources" )]
46  public AudioSource pointerAudioSource;
47  public AudioSource loopingAudioSource;
48  public AudioSource headAudioSource;
49  public AudioSource reticleAudioSource;
50 
51  [Header( "Sounds" )]
52  public AudioClip teleportSound;
53  public AudioClip pointerStartSound;
54  public AudioClip pointerLoopSound;
55  public AudioClip pointerStopSound;
56  public AudioClip goodHighlightSound;
57  public AudioClip badHighlightSound;
58 
59  [Header( "Debug" )]
60  public bool debugFloor = false;
61  public bool showOffsetReticle = false;
62  public Transform offsetReticleTransform;
63  public MeshRenderer floorDebugSphere;
64  public LineRenderer floorDebugLine;
65 
66  private LineRenderer pointerLineRenderer;
67  private GameObject teleportPointerObject;
68  private Transform pointerStartTransform;
69  private Hand pointerHand = null;
70  private Player player = null;
71  private TeleportArc teleportArc = null;
72 
73  private bool visible = false;
74 
75  private TeleportMarkerBase[] teleportMarkers;
76  private TeleportMarkerBase pointedAtTeleportMarker;
77  private TeleportMarkerBase teleportingToMarker;
78  private Vector3 pointedAtPosition;
79  private Vector3 prevPointedAtPosition;
80  private bool teleporting = false;
81  private float currentFadeTime = 0.0f;
82 
83  private float meshAlphaPercent = 1.0f;
84  private float pointerShowStartTime = 0.0f;
85  private float pointerHideStartTime = 0.0f;
86  private bool meshFading = false;
87  private float fullTintAlpha;
88 
89  private float invalidReticleMinScale = 0.2f;
90  private float invalidReticleMaxScale = 1.0f;
91  private float invalidReticleMinScaleDistance = 0.4f;
92  private float invalidReticleMaxScaleDistance = 2.0f;
93  private Vector3 invalidReticleScale = Vector3.one;
94  private Quaternion invalidReticleTargetRotation = Quaternion.identity;
95 
96  private Transform playAreaPreviewTransform;
97  private Transform[] playAreaPreviewCorners;
98  private Transform[] playAreaPreviewSides;
99 
100  private float loopingAudioMaxVolume = 0.0f;
101 
102  private Coroutine hintCoroutine = null;
103 
104  private bool originalHoverLockState = false;
105  private Interactable originalHoveringInteractable = null;
106  private AllowTeleportWhileAttachedToHand allowTeleportWhileAttached = null;
107 
108  private Vector3 startingFeetOffset = Vector3.zero;
109  private bool movedFeetFarEnough = false;
110 
111  SteamVR_Events.Action chaperoneInfoInitializedAction;
112 
113  // Events
114 
115  public static SteamVR_Events.Event< float > ChangeScene = new SteamVR_Events.Event< float >();
116  public static SteamVR_Events.Action< float > ChangeSceneAction( UnityAction< float > action ) { return new SteamVR_Events.Action< float >( ChangeScene, action ); }
117 
119  public static SteamVR_Events.Action< TeleportMarkerBase > PlayerAction( UnityAction< TeleportMarkerBase > action ) { return new SteamVR_Events.Action< TeleportMarkerBase >( Player, action ); }
120 
122  public static SteamVR_Events.Action< TeleportMarkerBase > PlayerPreAction( UnityAction< TeleportMarkerBase > action ) { return new SteamVR_Events.Action< TeleportMarkerBase >( PlayerPre, action ); }
123 
124  //-------------------------------------------------
125  private static Teleport _instance;
126  public static Teleport instance
127  {
128  get
129  {
130  if ( _instance == null )
131  {
132  _instance = GameObject.FindObjectOfType<Teleport>();
133  }
134 
135  return _instance;
136  }
137  }
138 
139 
140  //-------------------------------------------------
141  void Awake()
142  {
143  _instance = this;
144 
145  chaperoneInfoInitializedAction = ChaperoneInfo.InitializedAction( OnChaperoneInfoInitialized );
146 
147  pointerLineRenderer = GetComponentInChildren<LineRenderer>();
148  teleportPointerObject = pointerLineRenderer.gameObject;
149 
150  int tintColorID = Shader.PropertyToID( "_TintColor" );
151  fullTintAlpha = pointVisibleMaterial.GetColor( tintColorID ).a;
152 
153  teleportArc = GetComponent<TeleportArc>();
154  teleportArc.traceLayerMask = traceLayerMask;
155 
156  loopingAudioMaxVolume = loopingAudioSource.volume;
157 
158  playAreaPreviewCorner.SetActive( false );
159  playAreaPreviewSide.SetActive( false );
160 
161  float invalidReticleStartingScale = invalidReticleTransform.localScale.x;
162  invalidReticleMinScale *= invalidReticleStartingScale;
163  invalidReticleMaxScale *= invalidReticleStartingScale;
164  }
165 
166 
167  //-------------------------------------------------
168  void Start()
169  {
170  teleportMarkers = GameObject.FindObjectsOfType<TeleportMarkerBase>();
171 
172  HidePointer();
173 
174  player = InteractionSystem.Player.instance;
175 
176  if ( player == null )
177  {
178  Debug.LogError( "Teleport: No Player instance found in map." );
179  Destroy( this.gameObject );
180  return;
181  }
182 
183  CheckForSpawnPoint();
184 
185  Invoke( "ShowTeleportHint", 5.0f );
186  }
187 
188 
189  //-------------------------------------------------
190  void OnEnable()
191  {
192  chaperoneInfoInitializedAction.enabled = true;
193  OnChaperoneInfoInitialized(); // In case it's already initialized
194  }
195 
196 
197  //-------------------------------------------------
198  void OnDisable()
199  {
200  chaperoneInfoInitializedAction.enabled = false;
201  HidePointer();
202  }
203 
204 
205  //-------------------------------------------------
206  private void CheckForSpawnPoint()
207  {
208  foreach ( TeleportMarkerBase teleportMarker in teleportMarkers )
209  {
210  TeleportPoint teleportPoint = teleportMarker as TeleportPoint;
211  if ( teleportPoint && teleportPoint.playerSpawnPoint )
212  {
213  teleportingToMarker = teleportMarker;
214  TeleportPlayer();
215  break;
216  }
217  }
218  }
219 
220 
221  //-------------------------------------------------
222  public void HideTeleportPointer()
223  {
224  if ( pointerHand != null )
225  {
226  HidePointer();
227  }
228  }
229 
230 
231  //-------------------------------------------------
232  void Update()
233  {
234  Hand oldPointerHand = pointerHand;
235  Hand newPointerHand = null;
236 
237  foreach ( Hand hand in player.hands )
238  {
239  if ( visible )
240  {
241  if ( WasTeleportButtonReleased( hand ) )
242  {
243  if ( pointerHand == hand ) //This is the pointer hand
244  {
245  TryTeleportPlayer();
246  }
247  }
248  }
249 
250  if ( WasTeleportButtonPressed( hand ) )
251  {
252  newPointerHand = hand;
253  }
254  }
255 
256  //If something is attached to the hand that is preventing teleport
257  if ( allowTeleportWhileAttached && !allowTeleportWhileAttached.teleportAllowed )
258  {
259  HidePointer();
260  }
261  else
262  {
263  if ( !visible && newPointerHand != null )
264  {
265  //Begin showing the pointer
266  ShowPointer( newPointerHand, oldPointerHand );
267  }
268  else if ( visible )
269  {
270  if ( newPointerHand == null && !IsTeleportButtonDown( pointerHand ) )
271  {
272  //Hide the pointer
273  HidePointer();
274  }
275  else if ( newPointerHand != null )
276  {
277  //Move the pointer to a new hand
278  ShowPointer( newPointerHand, oldPointerHand );
279  }
280  }
281  }
282 
283  if ( visible )
284  {
285  UpdatePointer();
286 
287  if ( meshFading )
288  {
289  UpdateTeleportColors();
290  }
291 
292  if ( onActivateObjectTransform.gameObject.activeSelf && Time.time - pointerShowStartTime > activateObjectTime )
293  {
294  onActivateObjectTransform.gameObject.SetActive( false );
295  }
296  }
297  else
298  {
299  if ( onDeactivateObjectTransform.gameObject.activeSelf && Time.time - pointerHideStartTime > deactivateObjectTime )
300  {
301  onDeactivateObjectTransform.gameObject.SetActive( false );
302  }
303  }
304  }
305 
306 
307  //-------------------------------------------------
308  private void UpdatePointer()
309  {
310  Vector3 pointerStart = pointerStartTransform.position;
311  Vector3 pointerEnd;
312  Vector3 pointerDir = pointerStartTransform.forward;
313  bool hitSomething = false;
314  bool showPlayAreaPreview = false;
315  Vector3 playerFeetOffset = player.trackingOriginTransform.position - player.feetPositionGuess;
316 
317  Vector3 arcVelocity = pointerDir * arcDistance;
318 
319  TeleportMarkerBase hitTeleportMarker = null;
320 
321  //Check pointer angle
322  float dotUp = Vector3.Dot( pointerDir, Vector3.up );
323  float dotForward = Vector3.Dot( pointerDir, player.hmdTransform.forward );
324  bool pointerAtBadAngle = false;
325  if ( ( dotForward > 0 && dotUp > 0.75f ) || ( dotForward < 0.0f && dotUp > 0.5f ) )
326  {
327  pointerAtBadAngle = true;
328  }
329 
330  //Trace to see if the pointer hit anything
331  RaycastHit hitInfo;
332  teleportArc.SetArcData( pointerStart, arcVelocity, true, pointerAtBadAngle );
333  if ( teleportArc.DrawArc( out hitInfo ) )
334  {
335  hitSomething = true;
336  hitTeleportMarker = hitInfo.collider.GetComponentInParent<TeleportMarkerBase>();
337  }
338 
339  if ( pointerAtBadAngle )
340  {
341  hitTeleportMarker = null;
342  }
343 
344  HighlightSelected( hitTeleportMarker );
345 
346  if ( hitTeleportMarker != null ) //Hit a teleport marker
347  {
348  if ( hitTeleportMarker.locked )
349  {
350  teleportArc.SetColor( pointerLockedColor );
351 #if (UNITY_5_4)
352  pointerLineRenderer.SetColors( pointerLockedColor, pointerLockedColor );
353 #else
354  pointerLineRenderer.startColor = pointerLockedColor;
355  pointerLineRenderer.endColor = pointerLockedColor;
356 #endif
357  destinationReticleTransform.gameObject.SetActive( false );
358  }
359  else
360  {
361  teleportArc.SetColor( pointerValidColor );
362 #if (UNITY_5_4)
363  pointerLineRenderer.SetColors( pointerValidColor, pointerValidColor );
364 #else
365  pointerLineRenderer.startColor = pointerValidColor;
366  pointerLineRenderer.endColor = pointerValidColor;
367 #endif
368  destinationReticleTransform.gameObject.SetActive( hitTeleportMarker.showReticle );
369  }
370 
371  offsetReticleTransform.gameObject.SetActive( true );
372 
373  invalidReticleTransform.gameObject.SetActive( false );
374 
375  pointedAtTeleportMarker = hitTeleportMarker;
376  pointedAtPosition = hitInfo.point;
377 
378  if ( showPlayAreaMarker )
379  {
380  //Show the play area marker if this is a teleport area
381  TeleportArea teleportArea = pointedAtTeleportMarker as TeleportArea;
382  if ( teleportArea != null && !teleportArea.locked && playAreaPreviewTransform != null )
383  {
384  Vector3 offsetToUse = playerFeetOffset;
385 
386  //Adjust the actual offset to prevent the play area marker from moving too much
387  if ( !movedFeetFarEnough )
388  {
389  float distanceFromStartingOffset = Vector3.Distance( playerFeetOffset, startingFeetOffset );
390  if ( distanceFromStartingOffset < 0.1f )
391  {
392  offsetToUse = startingFeetOffset;
393  }
394  else if ( distanceFromStartingOffset < 0.4f )
395  {
396  offsetToUse = Vector3.Lerp( startingFeetOffset, playerFeetOffset, ( distanceFromStartingOffset - 0.1f ) / 0.3f );
397  }
398  else
399  {
400  movedFeetFarEnough = true;
401  }
402  }
403 
404  playAreaPreviewTransform.position = pointedAtPosition + offsetToUse;
405 
406  showPlayAreaPreview = true;
407  }
408  }
409 
410  pointerEnd = hitInfo.point;
411  }
412  else //Hit neither
413  {
414  destinationReticleTransform.gameObject.SetActive( false );
415  offsetReticleTransform.gameObject.SetActive( false );
416 
417  teleportArc.SetColor( pointerInvalidColor );
418 #if (UNITY_5_4)
419  pointerLineRenderer.SetColors( pointerInvalidColor, pointerInvalidColor );
420 #else
421  pointerLineRenderer.startColor = pointerInvalidColor;
422  pointerLineRenderer.endColor = pointerInvalidColor;
423 #endif
424  invalidReticleTransform.gameObject.SetActive( !pointerAtBadAngle );
425 
426  //Orient the invalid reticle to the normal of the trace hit point
427  Vector3 normalToUse = hitInfo.normal;
428  float angle = Vector3.Angle( hitInfo.normal, Vector3.up );
429  if ( angle < 15.0f )
430  {
431  normalToUse = Vector3.up;
432  }
433  invalidReticleTargetRotation = Quaternion.FromToRotation( Vector3.up, normalToUse );
434  invalidReticleTransform.rotation = Quaternion.Slerp( invalidReticleTransform.rotation, invalidReticleTargetRotation, 0.1f );
435 
436  //Scale the invalid reticle based on the distance from the player
437  float distanceFromPlayer = Vector3.Distance( hitInfo.point, player.hmdTransform.position );
438  float invalidReticleCurrentScale = Util.RemapNumberClamped( distanceFromPlayer, invalidReticleMinScaleDistance, invalidReticleMaxScaleDistance, invalidReticleMinScale, invalidReticleMaxScale );
439  invalidReticleScale.x = invalidReticleCurrentScale;
440  invalidReticleScale.y = invalidReticleCurrentScale;
441  invalidReticleScale.z = invalidReticleCurrentScale;
442  invalidReticleTransform.transform.localScale = invalidReticleScale;
443 
444  pointedAtTeleportMarker = null;
445 
446  if ( hitSomething )
447  {
448  pointerEnd = hitInfo.point;
449  }
450  else
451  {
452  pointerEnd = teleportArc.GetArcPositionAtTime( teleportArc.arcDuration );
453  }
454 
455  //Debug floor
456  if ( debugFloor )
457  {
458  floorDebugSphere.gameObject.SetActive( false );
459  floorDebugLine.gameObject.SetActive( false );
460  }
461  }
462 
463  if ( playAreaPreviewTransform != null )
464  {
465  playAreaPreviewTransform.gameObject.SetActive( showPlayAreaPreview );
466  }
467 
468  if ( !showOffsetReticle )
469  {
470  offsetReticleTransform.gameObject.SetActive( false );
471  }
472 
473  destinationReticleTransform.position = pointedAtPosition;
474  invalidReticleTransform.position = pointerEnd;
475  onActivateObjectTransform.position = pointerEnd;
476  onDeactivateObjectTransform.position = pointerEnd;
477  offsetReticleTransform.position = pointerEnd - playerFeetOffset;
478 
479  reticleAudioSource.transform.position = pointedAtPosition;
480 
481  pointerLineRenderer.SetPosition( 0, pointerStart );
482  pointerLineRenderer.SetPosition( 1, pointerEnd );
483  }
484 
485 
486  //-------------------------------------------------
487  void FixedUpdate()
488  {
489  if ( !visible )
490  {
491  return;
492  }
493 
494  if ( debugFloor )
495  {
496  //Debug floor
497  TeleportArea teleportArea = pointedAtTeleportMarker as TeleportArea;
498  if ( teleportArea != null )
499  {
500  if ( floorFixupMaximumTraceDistance > 0.0f )
501  {
502  floorDebugSphere.gameObject.SetActive( true );
503  floorDebugLine.gameObject.SetActive( true );
504 
505  RaycastHit raycastHit;
506  Vector3 traceDir = Vector3.down;
507  traceDir.x = 0.01f;
508  if ( Physics.Raycast( pointedAtPosition + 0.05f * traceDir, traceDir, out raycastHit, floorFixupMaximumTraceDistance, floorFixupTraceLayerMask ) )
509  {
510  floorDebugSphere.transform.position = raycastHit.point;
511  floorDebugSphere.material.color = Color.green;
512 #if (UNITY_5_4)
513  floorDebugLine.SetColors( Color.green, Color.green );
514 #else
515  floorDebugLine.startColor = Color.green;
516  floorDebugLine.endColor = Color.green;
517 #endif
518  floorDebugLine.SetPosition( 0, pointedAtPosition );
519  floorDebugLine.SetPosition( 1, raycastHit.point );
520  }
521  else
522  {
523  Vector3 rayEnd = pointedAtPosition + ( traceDir * floorFixupMaximumTraceDistance );
524  floorDebugSphere.transform.position = rayEnd;
525  floorDebugSphere.material.color = Color.red;
526 #if (UNITY_5_4)
527  floorDebugLine.SetColors( Color.red, Color.red );
528 #else
529  floorDebugLine.startColor = Color.red;
530  floorDebugLine.endColor = Color.red;
531 #endif
532  floorDebugLine.SetPosition( 0, pointedAtPosition );
533  floorDebugLine.SetPosition( 1, rayEnd );
534  }
535  }
536  }
537  }
538  }
539 
540 
541  //-------------------------------------------------
542  private void OnChaperoneInfoInitialized()
543  {
544  ChaperoneInfo chaperone = ChaperoneInfo.instance;
545 
546  if ( chaperone.initialized && chaperone.roomscale )
547  {
548  //Set up the render model for the play area bounds
549 
550  if ( playAreaPreviewTransform == null )
551  {
552  playAreaPreviewTransform = new GameObject( "PlayAreaPreviewTransform" ).transform;
553  playAreaPreviewTransform.parent = transform;
554  Util.ResetTransform( playAreaPreviewTransform );
555 
556  playAreaPreviewCorner.SetActive( true );
557  playAreaPreviewCorners = new Transform[4];
558  playAreaPreviewCorners[0] = playAreaPreviewCorner.transform;
559  playAreaPreviewCorners[1] = Instantiate( playAreaPreviewCorners[0] );
560  playAreaPreviewCorners[2] = Instantiate( playAreaPreviewCorners[0] );
561  playAreaPreviewCorners[3] = Instantiate( playAreaPreviewCorners[0] );
562 
563  playAreaPreviewCorners[0].transform.parent = playAreaPreviewTransform;
564  playAreaPreviewCorners[1].transform.parent = playAreaPreviewTransform;
565  playAreaPreviewCorners[2].transform.parent = playAreaPreviewTransform;
566  playAreaPreviewCorners[3].transform.parent = playAreaPreviewTransform;
567 
568  playAreaPreviewSide.SetActive( true );
569  playAreaPreviewSides = new Transform[4];
570  playAreaPreviewSides[0] = playAreaPreviewSide.transform;
571  playAreaPreviewSides[1] = Instantiate( playAreaPreviewSides[0] );
572  playAreaPreviewSides[2] = Instantiate( playAreaPreviewSides[0] );
573  playAreaPreviewSides[3] = Instantiate( playAreaPreviewSides[0] );
574 
575  playAreaPreviewSides[0].transform.parent = playAreaPreviewTransform;
576  playAreaPreviewSides[1].transform.parent = playAreaPreviewTransform;
577  playAreaPreviewSides[2].transform.parent = playAreaPreviewTransform;
578  playAreaPreviewSides[3].transform.parent = playAreaPreviewTransform;
579  }
580 
581  float x = chaperone.playAreaSizeX;
582  float z = chaperone.playAreaSizeZ;
583 
584  playAreaPreviewSides[0].localPosition = new Vector3( 0.0f, 0.0f, 0.5f * z - 0.25f );
585  playAreaPreviewSides[1].localPosition = new Vector3( 0.0f, 0.0f, -0.5f * z + 0.25f );
586  playAreaPreviewSides[2].localPosition = new Vector3( 0.5f * x - 0.25f, 0.0f, 0.0f );
587  playAreaPreviewSides[3].localPosition = new Vector3( -0.5f * x + 0.25f, 0.0f, 0.0f );
588 
589  playAreaPreviewSides[0].localScale = new Vector3( x - 0.5f, 1.0f, 1.0f );
590  playAreaPreviewSides[1].localScale = new Vector3( x - 0.5f, 1.0f, 1.0f );
591  playAreaPreviewSides[2].localScale = new Vector3( z - 0.5f, 1.0f, 1.0f );
592  playAreaPreviewSides[3].localScale = new Vector3( z - 0.5f, 1.0f, 1.0f );
593 
594  playAreaPreviewSides[0].localRotation = Quaternion.Euler( 0.0f, 0.0f, 0.0f );
595  playAreaPreviewSides[1].localRotation = Quaternion.Euler( 0.0f, 180.0f, 0.0f );
596  playAreaPreviewSides[2].localRotation = Quaternion.Euler( 0.0f, 90.0f, 0.0f );
597  playAreaPreviewSides[3].localRotation = Quaternion.Euler( 0.0f, 270.0f, 0.0f );
598 
599  playAreaPreviewCorners[0].localPosition = new Vector3( 0.5f * x - 0.25f, 0.0f, 0.5f * z - 0.25f );
600  playAreaPreviewCorners[1].localPosition = new Vector3( 0.5f * x - 0.25f, 0.0f, -0.5f * z + 0.25f );
601  playAreaPreviewCorners[2].localPosition = new Vector3( -0.5f * x + 0.25f, 0.0f, -0.5f * z + 0.25f );
602  playAreaPreviewCorners[3].localPosition = new Vector3( -0.5f * x + 0.25f, 0.0f, 0.5f * z - 0.25f );
603 
604  playAreaPreviewCorners[0].localRotation = Quaternion.Euler( 0.0f, 0.0f, 0.0f );
605  playAreaPreviewCorners[1].localRotation = Quaternion.Euler( 0.0f, 90.0f, 0.0f );
606  playAreaPreviewCorners[2].localRotation = Quaternion.Euler( 0.0f, 180.0f, 0.0f );
607  playAreaPreviewCorners[3].localRotation = Quaternion.Euler( 0.0f, 270.0f, 0.0f );
608 
609  playAreaPreviewTransform.gameObject.SetActive( false );
610  }
611  }
612 
613 
614  //-------------------------------------------------
615  private void HidePointer()
616  {
617  if ( visible )
618  {
619  pointerHideStartTime = Time.time;
620  }
621 
622  visible = false;
623  if ( pointerHand )
624  {
625  if ( ShouldOverrideHoverLock() )
626  {
627  //Restore the original hovering interactable on the hand
628  if ( originalHoverLockState == true )
629  {
630  pointerHand.HoverLock( originalHoveringInteractable );
631  }
632  else
633  {
634  pointerHand.HoverUnlock( null );
635  }
636  }
637 
638  //Stop looping sound
639  loopingAudioSource.Stop();
640  PlayAudioClip( pointerAudioSource, pointerStopSound );
641  }
642  teleportPointerObject.SetActive( false );
643 
644  teleportArc.Hide();
645 
646  foreach ( TeleportMarkerBase teleportMarker in teleportMarkers )
647  {
648  if ( teleportMarker != null && teleportMarker.markerActive && teleportMarker.gameObject != null )
649  {
650  teleportMarker.gameObject.SetActive( false );
651  }
652  }
653 
654  destinationReticleTransform.gameObject.SetActive( false );
655  invalidReticleTransform.gameObject.SetActive( false );
656  offsetReticleTransform.gameObject.SetActive( false );
657 
658  if ( playAreaPreviewTransform != null )
659  {
660  playAreaPreviewTransform.gameObject.SetActive( false );
661  }
662 
663  if ( onActivateObjectTransform.gameObject.activeSelf )
664  {
665  onActivateObjectTransform.gameObject.SetActive( false );
666  }
667  onDeactivateObjectTransform.gameObject.SetActive( true );
668 
669  pointerHand = null;
670  }
671 
672 
673  //-------------------------------------------------
674  private void ShowPointer( Hand newPointerHand, Hand oldPointerHand )
675  {
676  if ( !visible )
677  {
678  pointedAtTeleportMarker = null;
679  pointerShowStartTime = Time.time;
680  visible = true;
681  meshFading = true;
682 
683  teleportPointerObject.SetActive( false );
684  teleportArc.Show();
685 
686  foreach ( TeleportMarkerBase teleportMarker in teleportMarkers )
687  {
688  if ( teleportMarker.markerActive && teleportMarker.ShouldActivate( player.feetPositionGuess ) )
689  {
690  teleportMarker.gameObject.SetActive( true );
691  teleportMarker.Highlight( false );
692  }
693  }
694 
695  startingFeetOffset = player.trackingOriginTransform.position - player.feetPositionGuess;
696  movedFeetFarEnough = false;
697 
698  if ( onDeactivateObjectTransform.gameObject.activeSelf )
699  {
700  onDeactivateObjectTransform.gameObject.SetActive( false );
701  }
702  onActivateObjectTransform.gameObject.SetActive( true );
703 
704  loopingAudioSource.clip = pointerLoopSound;
705  loopingAudioSource.loop = true;
706  loopingAudioSource.Play();
707  loopingAudioSource.volume = 0.0f;
708  }
709 
710 
711  if ( oldPointerHand )
712  {
713  if ( ShouldOverrideHoverLock() )
714  {
715  //Restore the original hovering interactable on the hand
716  if ( originalHoverLockState == true )
717  {
718  oldPointerHand.HoverLock( originalHoveringInteractable );
719  }
720  else
721  {
722  oldPointerHand.HoverUnlock( null );
723  }
724  }
725  }
726 
727  pointerHand = newPointerHand;
728 
729  if ( visible && oldPointerHand != pointerHand )
730  {
731  PlayAudioClip( pointerAudioSource, pointerStartSound );
732  }
733 
734  if ( pointerHand )
735  {
736  pointerStartTransform = GetPointerStartTransform( pointerHand );
737 
738  if ( pointerHand.currentAttachedObject != null )
739  {
740  allowTeleportWhileAttached = pointerHand.currentAttachedObject.GetComponent<AllowTeleportWhileAttachedToHand>();
741  }
742 
743  //Keep track of any existing hovering interactable on the hand
744  originalHoverLockState = pointerHand.hoverLocked;
745  originalHoveringInteractable = pointerHand.hoveringInteractable;
746 
747  if ( ShouldOverrideHoverLock() )
748  {
749  pointerHand.HoverLock( null );
750  }
751 
752  pointerAudioSource.transform.SetParent( pointerStartTransform );
753  pointerAudioSource.transform.localPosition = Vector3.zero;
754 
755  loopingAudioSource.transform.SetParent( pointerStartTransform );
756  loopingAudioSource.transform.localPosition = Vector3.zero;
757  }
758  }
759 
760 
761  //-------------------------------------------------
762  private void UpdateTeleportColors()
763  {
764  float deltaTime = Time.time - pointerShowStartTime;
765  if ( deltaTime > meshFadeTime )
766  {
767  meshAlphaPercent = 1.0f;
768  meshFading = false;
769  }
770  else
771  {
772  meshAlphaPercent = Mathf.Lerp( 0.0f, 1.0f, deltaTime / meshFadeTime );
773  }
774 
775  //Tint color for the teleport points
776  foreach ( TeleportMarkerBase teleportMarker in teleportMarkers )
777  {
778  teleportMarker.SetAlpha( fullTintAlpha * meshAlphaPercent, meshAlphaPercent );
779  }
780  }
781 
782 
783  //-------------------------------------------------
784  private void PlayAudioClip( AudioSource source, AudioClip clip )
785  {
786  source.clip = clip;
787  source.Play();
788  }
789 
790 
791  //-------------------------------------------------
792  private void PlayPointerHaptic( bool validLocation )
793  {
794  if ( pointerHand.controller != null )
795  {
796  if ( validLocation )
797  {
798  pointerHand.controller.TriggerHapticPulse( 800 );
799  }
800  else
801  {
802  pointerHand.controller.TriggerHapticPulse( 100 );
803  }
804  }
805  }
806 
807 
808  //-------------------------------------------------
809  private void TryTeleportPlayer()
810  {
811  if ( visible && !teleporting )
812  {
813  if ( pointedAtTeleportMarker != null && pointedAtTeleportMarker.locked == false )
814  {
815  //Pointing at an unlocked teleport marker
816  teleportingToMarker = pointedAtTeleportMarker;
817  InitiateTeleportFade();
818 
819  CancelTeleportHint();
820  }
821  }
822  }
823 
824 
825  //-------------------------------------------------
826  private void InitiateTeleportFade()
827  {
828  teleporting = true;
829 
830  currentFadeTime = teleportFadeTime;
831 
832  TeleportPoint teleportPoint = teleportingToMarker as TeleportPoint;
833  if ( teleportPoint != null && teleportPoint.teleportType == TeleportPoint.TeleportPointType.SwitchToNewScene )
834  {
835  currentFadeTime *= 3.0f;
836  Teleport.ChangeScene.Send( currentFadeTime );
837  }
838 
839  SteamVR_Fade.Start( Color.clear, 0 );
840  SteamVR_Fade.Start( Color.black, currentFadeTime );
841 
842  headAudioSource.transform.SetParent( player.hmdTransform );
843  headAudioSource.transform.localPosition = Vector3.zero;
844  PlayAudioClip( headAudioSource, teleportSound );
845 
846  Invoke( "TeleportPlayer", currentFadeTime );
847  }
848 
849 
850  //-------------------------------------------------
851  private void TeleportPlayer()
852  {
853  teleporting = false;
854 
855  Teleport.PlayerPre.Send( pointedAtTeleportMarker );
856 
857  SteamVR_Fade.Start( Color.clear, currentFadeTime );
858 
859  TeleportPoint teleportPoint = teleportingToMarker as TeleportPoint;
860  Vector3 teleportPosition = pointedAtPosition;
861 
862  if ( teleportPoint != null )
863  {
864  teleportPosition = teleportPoint.transform.position;
865 
866  //Teleport to a new scene
867  if ( teleportPoint.teleportType == TeleportPoint.TeleportPointType.SwitchToNewScene )
868  {
869  teleportPoint.TeleportToScene();
870  return;
871  }
872  }
873 
874  // Find the actual floor position below the navigation mesh
875  TeleportArea teleportArea = teleportingToMarker as TeleportArea;
876  if ( teleportArea != null )
877  {
878  if ( floorFixupMaximumTraceDistance > 0.0f )
879  {
880  RaycastHit raycastHit;
881  if ( Physics.Raycast( teleportPosition + 0.05f * Vector3.down, Vector3.down, out raycastHit, floorFixupMaximumTraceDistance, floorFixupTraceLayerMask ) )
882  {
883  teleportPosition = raycastHit.point;
884  }
885  }
886  }
887 
888  if ( teleportingToMarker.ShouldMovePlayer() )
889  {
890  Vector3 playerFeetOffset = player.trackingOriginTransform.position - player.feetPositionGuess;
891  player.trackingOriginTransform.position = teleportPosition + playerFeetOffset;
892  }
893  else
894  {
895  teleportingToMarker.TeleportPlayer( pointedAtPosition );
896  }
897 
898  Teleport.Player.Send( pointedAtTeleportMarker );
899  }
900 
901 
902  //-------------------------------------------------
903  private void HighlightSelected( TeleportMarkerBase hitTeleportMarker )
904  {
905  if ( pointedAtTeleportMarker != hitTeleportMarker ) //Pointing at a new teleport marker
906  {
907  if ( pointedAtTeleportMarker != null )
908  {
909  pointedAtTeleportMarker.Highlight( false );
910  }
911 
912  if ( hitTeleportMarker != null )
913  {
914  hitTeleportMarker.Highlight( true );
915 
916  prevPointedAtPosition = pointedAtPosition;
917  PlayPointerHaptic( !hitTeleportMarker.locked );
918 
919  PlayAudioClip( reticleAudioSource, goodHighlightSound );
920 
921  loopingAudioSource.volume = loopingAudioMaxVolume;
922  }
923  else if ( pointedAtTeleportMarker != null )
924  {
925  PlayAudioClip( reticleAudioSource, badHighlightSound );
926 
927  loopingAudioSource.volume = 0.0f;
928  }
929  }
930  else if ( hitTeleportMarker != null ) //Pointing at the same teleport marker
931  {
932  if ( Vector3.Distance( prevPointedAtPosition, pointedAtPosition ) > 1.0f )
933  {
934  prevPointedAtPosition = pointedAtPosition;
935  PlayPointerHaptic( !hitTeleportMarker.locked );
936  }
937  }
938  }
939 
940 
941  //-------------------------------------------------
942  public void ShowTeleportHint()
943  {
944  CancelTeleportHint();
945 
946  hintCoroutine = StartCoroutine( TeleportHintCoroutine() );
947  }
948 
949 
950  //-------------------------------------------------
951  public void CancelTeleportHint()
952  {
953  if ( hintCoroutine != null )
954  {
955  foreach ( Hand hand in player.hands )
956  {
957  ControllerButtonHints.HideTextHint( hand, EVRButtonId.k_EButton_SteamVR_Touchpad );
958  }
959 
960  StopCoroutine( hintCoroutine );
961  hintCoroutine = null;
962  }
963 
964  CancelInvoke( "ShowTeleportHint" );
965  }
966 
967 
968  //-------------------------------------------------
969  private IEnumerator TeleportHintCoroutine()
970  {
971  float prevBreakTime = Time.time;
972  float prevHapticPulseTime = Time.time;
973 
974  while ( true )
975  {
976  bool pulsed = false;
977 
978  //Show the hint on each eligible hand
979  foreach ( Hand hand in player.hands )
980  {
981  bool showHint = IsEligibleForTeleport( hand );
982  bool isShowingHint = !string.IsNullOrEmpty( ControllerButtonHints.GetActiveHintText( hand, EVRButtonId.k_EButton_SteamVR_Touchpad ) );
983  if ( showHint )
984  {
985  if ( !isShowingHint )
986  {
987  ControllerButtonHints.ShowTextHint( hand, EVRButtonId.k_EButton_SteamVR_Touchpad, "Teleport" );
988  prevBreakTime = Time.time;
989  prevHapticPulseTime = Time.time;
990  }
991 
992  if ( Time.time > prevHapticPulseTime + 0.05f )
993  {
994  //Haptic pulse for a few seconds
995  pulsed = true;
996 
997  hand.controller.TriggerHapticPulse( 500 );
998  }
999  }
1000  else if ( !showHint && isShowingHint )
1001  {
1002  ControllerButtonHints.HideTextHint( hand, EVRButtonId.k_EButton_SteamVR_Touchpad );
1003  }
1004  }
1005 
1006  if ( Time.time > prevBreakTime + 3.0f )
1007  {
1008  //Take a break for a few seconds
1009  yield return new WaitForSeconds( 3.0f );
1010 
1011  prevBreakTime = Time.time;
1012  }
1013 
1014  if ( pulsed )
1015  {
1016  prevHapticPulseTime = Time.time;
1017  }
1018 
1019  yield return null;
1020  }
1021  }
1022 
1023 
1024  //-------------------------------------------------
1025  public bool IsEligibleForTeleport( Hand hand )
1026  {
1027  if ( hand == null )
1028  {
1029  return false;
1030  }
1031 
1032  if ( !hand.gameObject.activeInHierarchy )
1033  {
1034  return false;
1035  }
1036 
1037  if ( hand.hoveringInteractable != null )
1038  {
1039  return false;
1040  }
1041 
1042  if ( hand.noSteamVRFallbackCamera == null )
1043  {
1044  if ( hand.controller == null )
1045  {
1046  return false;
1047  }
1048 
1049  //Something is attached to the hand
1050  if ( hand.currentAttachedObject != null )
1051  {
1052  AllowTeleportWhileAttachedToHand allowTeleportWhileAttachedToHand = hand.currentAttachedObject.GetComponent<AllowTeleportWhileAttachedToHand>();
1053 
1054  if ( allowTeleportWhileAttachedToHand != null && allowTeleportWhileAttachedToHand.teleportAllowed == true )
1055  {
1056  return true;
1057  }
1058  else
1059  {
1060  return false;
1061  }
1062  }
1063  }
1064 
1065  return true;
1066  }
1067 
1068 
1069  //-------------------------------------------------
1070  private bool ShouldOverrideHoverLock()
1071  {
1072  if ( !allowTeleportWhileAttached || allowTeleportWhileAttached.overrideHoverLock )
1073  {
1074  return true;
1075  }
1076 
1077  return false;
1078  }
1079 
1080 
1081  //-------------------------------------------------
1082  private bool WasTeleportButtonReleased( Hand hand )
1083  {
1084  if ( IsEligibleForTeleport( hand ) )
1085  {
1086  if ( hand.noSteamVRFallbackCamera != null )
1087  {
1088  return Input.GetKeyUp( KeyCode.T );
1089  }
1090  else
1091  {
1092  return hand.controller.GetPressUp( SteamVR_Controller.ButtonMask.Touchpad );
1093  }
1094  }
1095 
1096  return false;
1097  }
1098 
1099 
1100  //-------------------------------------------------
1101  private bool IsTeleportButtonDown( Hand hand )
1102  {
1103  if ( IsEligibleForTeleport( hand ) )
1104  {
1105  if ( hand.noSteamVRFallbackCamera != null )
1106  {
1107  return Input.GetKey( KeyCode.T );
1108  }
1109  else
1110  {
1111  return hand.controller.GetPress( SteamVR_Controller.ButtonMask.Touchpad );
1112  }
1113  }
1114 
1115  return false;
1116  }
1117 
1118 
1119  //-------------------------------------------------
1120  private bool WasTeleportButtonPressed( Hand hand )
1121  {
1122  if ( IsEligibleForTeleport( hand ) )
1123  {
1124  if ( hand.noSteamVRFallbackCamera != null )
1125  {
1126  return Input.GetKeyDown( KeyCode.T );
1127  }
1128  else
1129  {
1130  return hand.controller.GetPressDown( SteamVR_Controller.ButtonMask.Touchpad );
1131  }
1132  }
1133 
1134  return false;
1135  }
1136 
1137 
1138  //-------------------------------------------------
1139  private Transform GetPointerStartTransform( Hand hand )
1140  {
1141  if ( hand.noSteamVRFallbackCamera != null )
1142  {
1143  return hand.noSteamVRFallbackCamera.transform;
1144  }
1145  else
1146  {
1147  return pointerHand.GetAttachmentTransform( "Attach_ControllerTip" );
1148  }
1149  }
1150  }
1151 }