AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
MixedRealityTeleport.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 
4 using System;
5 using UnityEngine;
6 
7 #if UNITY_2017_2_OR_NEWER
8 using UnityEngine.XR;
9 #if UNITY_WSA
10 using UnityEngine.XR.WSA;
11 using UnityEngine.XR.WSA.Input;
12 #endif
13 #else
14 using UnityEngine.VR;
15 #if UNITY_WSA
16 using UnityEngine.VR.WSA.Input;
17 #endif
18 #endif
19 
20 namespace HoloToolkit.Unity.InputModule
21 {
25  [RequireComponent(typeof(SetGlobalListener))]
26  public class MixedRealityTeleport : Singleton<MixedRealityTeleport>, IControllerInputHandler
27  {
28  [Tooltip("Name of the thumbstick axis to check for teleport and strafe.")]
29  public XboxControllerMappingTypes HorizontalStrafe = XboxControllerMappingTypes.XboxLeftStickHorizontal;
30 
31  [Tooltip("Name of the thumbstick axis to check for movement forwards and backwards.")]
32  public XboxControllerMappingTypes ForwardMovement = XboxControllerMappingTypes.XboxLeftStickVertical;
33 
34  [Tooltip("Name of the thumbstick axis to check for rotation.")]
35  public XboxControllerMappingTypes HorizontalRotation = XboxControllerMappingTypes.XboxRightStickHorizontal;
36 
37  [Tooltip("Name of the thumbstick axis to check for rotation.")]
38  public XboxControllerMappingTypes VerticalRotation = XboxControllerMappingTypes.XboxRightStickVertical;
39 
40  [Tooltip("Custom Input Mapping for horizontal teleport and strafe")]
42 
43  [Tooltip("Name of the thumbstick axis to check for movement forwards and backwards.")]
45 
46  [Tooltip("Custom Input Mapping for horizontal rotation")]
48 
49  [Tooltip("Custom Input Mapping for vertical rotation")]
50  public string RightThumbstickY = InputMappingAxisUtility.CONTROLLER_RIGHT_STICK_VERTICAL;
51 
52  public bool EnableTeleport = true;
53  public bool EnableRotation = true;
54  public bool EnableStrafe = true;
55 
56  [Tooltip("Makes sure you don't get put 'on top' of holograms, just on the floor. If true, your height won't change as a result of a teleport.")]
57  public bool StayOnTheFloor = false;
58 
59  public float RotationSize = 45.0f;
60  public float StrafeAmount = 0.5f;
61 
62  [SerializeField]
63  private GameObject teleportMarker;
64  private Animator animationController;
65 
66  [SerializeField]
67  private bool useCustomMapping = false;
68 
73  private FadeManager fadeControl;
74 
75  private bool isTeleportValid;
76  private IPointingSource currentPointingSource;
77  private uint currentSourceId;
78 
79  private void Start()
80  {
81  // If we're on the HoloLens or no device is present,
82  // remove this component.
83 #if UNITY_2017_2_OR_NEWER
84  if (!XRDevice.isPresent
85 #if UNITY_WSA
86  || !HolographicSettings.IsDisplayOpaque
87 #endif
88  )
89 #else
90  if (VRDevice.isPresent)
91 #endif
92  {
93  Destroy(this);
94  return;
95  }
96 
97  // FadeManager isn't checked unless we're in a
98  // setup where it might be supported.
100 
101  fadeControl = FadeManager.Instance;
102 
103  // If the FadeManager is missing,
104  // remove this component.
105  if (fadeControl == null)
106  {
107  Destroy(this);
108  return;
109  }
110 
111  if (teleportMarker != null)
112  {
113  teleportMarker = Instantiate(teleportMarker);
114  teleportMarker.SetActive(false);
115 
116  animationController = teleportMarker.GetComponentInChildren<Animator>();
117  if (animationController != null)
118  {
119  animationController.StopPlayback();
120  }
121  }
122  }
123 
124  private void Update()
125  {
126 #if UNITY_WSA
127  if (InteractionManager.numSourceStates == 0)
128  {
129  HandleGamepad();
130  }
131 #endif
132 
133  if (currentPointingSource != null)
134  {
135  PositionMarker();
136  }
137  }
138 
139  private void HandleGamepad()
140  {
141  if (EnableTeleport && !fadeControl.Busy)
142  {
143  float leftX = Input.GetAxis(useCustomMapping ? LeftThumbstickX : XboxControllerMapping.GetMapping(HorizontalStrafe));
144  float leftY = Input.GetAxis(useCustomMapping ? LeftThumbstickY : XboxControllerMapping.GetMapping(ForwardMovement));
145 
146  if (currentPointingSource == null && leftY > 0.8 && Math.Abs(leftX) < 0.3)
147  {
148  if (FocusManager.Instance.TryGetSinglePointer(out currentPointingSource))
149  {
150  StartTeleport();
151  }
152  }
153  else if (currentPointingSource != null && new Vector2(leftX, leftY).magnitude < 0.2)
154  {
155  FinishTeleport();
156  }
157  }
158 
159  if (EnableStrafe && currentPointingSource == null && !fadeControl.Busy)
160  {
161  float leftX = Input.GetAxis(useCustomMapping ? LeftThumbstickX : XboxControllerMapping.GetMapping(HorizontalStrafe));
162  float leftY = Input.GetAxis(useCustomMapping ? LeftThumbstickY : XboxControllerMapping.GetMapping(ForwardMovement));
163 
164  if (leftX < -0.8 && Math.Abs(leftY) < 0.3)
165  {
166  DoStrafe(Vector3.left * StrafeAmount);
167  }
168  else if (leftX > 0.8 && Math.Abs(leftY) < 0.3)
169  {
170  DoStrafe(Vector3.right * StrafeAmount);
171  }
172  else if (leftY < -0.8 && Math.Abs(leftX) < 0.3)
173  {
174  DoStrafe(Vector3.back * StrafeAmount);
175  }
176  }
177 
178  if (EnableRotation && currentPointingSource == null && !fadeControl.Busy)
179  {
180  float rightX = Input.GetAxis(useCustomMapping ? RightThumbstickX : XboxControllerMapping.GetMapping(HorizontalRotation));
181  float rightY = Input.GetAxis(useCustomMapping ? RightThumbstickY : XboxControllerMapping.GetMapping(VerticalRotation));
182 
183  if (rightX < -0.8 && Math.Abs(rightY) < 0.3)
184  {
185  DoRotation(-RotationSize);
186  }
187  else if (rightX > 0.8 && Math.Abs(rightY) < 0.3)
188  {
189  DoRotation(RotationSize);
190  }
191  }
192  }
193 
195  {
196  if (eventData.PressType == InteractionSourcePressInfo.Thumbstick)
197  {
198  if (EnableTeleport)
199  {
200  if (currentPointingSource == null && eventData.Position.y > 0.8 && Math.Abs(eventData.Position.x) < 0.3)
201  {
202  if (FocusManager.Instance.TryGetPointingSource(eventData, out currentPointingSource))
203  {
204  currentSourceId = eventData.SourceId;
205  StartTeleport();
206  }
207  }
208  else if (currentPointingSource != null && currentSourceId == eventData.SourceId && eventData.Position.magnitude < 0.2)
209  {
210  FinishTeleport();
211  }
212  }
213 
214  if (EnableStrafe && currentPointingSource == null)
215  {
216  if (eventData.Position.y < -0.8 && Math.Abs(eventData.Position.x) < 0.3)
217  {
218  DoStrafe(Vector3.back * StrafeAmount);
219  }
220  }
221 
222  if (EnableRotation && currentPointingSource == null)
223  {
224  if (eventData.Position.x < -0.8 && Math.Abs(eventData.Position.y) < 0.3)
225  {
226  DoRotation(-RotationSize);
227  }
228  else if (eventData.Position.x > 0.8 && Math.Abs(eventData.Position.y) < 0.3)
229  {
230  DoRotation(RotationSize);
231  }
232  }
233  }
234  }
235 
236  public void StartTeleport()
237  {
238  if (currentPointingSource != null && !fadeControl.Busy)
239  {
240  EnableMarker();
241  PositionMarker();
242  }
243  }
244 
245  private void FinishTeleport()
246  {
247  if (currentPointingSource != null)
248  {
249  currentPointingSource = null;
250 
251  if (isTeleportValid)
252  {
253  fadeControl.DoFade(0.25f, 0.5f, () =>
254  {
255  SetWorldPosition(teleportMarker.transform.position);
256  }, null);
257  }
258 
259  DisableMarker();
260  }
261  }
262 
263  public void DoRotation(float rotationAmount)
264  {
265  if (rotationAmount != 0 && !fadeControl.Busy)
266  {
267  fadeControl.DoFade(
268  0.25f, // Fade out time
269  0.25f, // Fade in time
270  () => // Action after fade out
271  {
272  transform.RotateAround(CameraCache.Main.transform.position, Vector3.up, rotationAmount);
273  }, null); // Action after fade in
274  }
275  }
276 
277  public void DoStrafe(Vector3 strafeAmount)
278  {
279  if (strafeAmount.magnitude != 0 && !fadeControl.Busy)
280  {
281  fadeControl.DoFade(
282  0.25f, // Fade out time
283  0.25f, // Fade in time
284  () => // Action after fade out
285  {
286  Transform transformToRotate = CameraCache.Main.transform;
287  transformToRotate.rotation = Quaternion.Euler(0, transformToRotate.rotation.eulerAngles.y, 0);
288  transform.Translate(strafeAmount, CameraCache.Main.transform);
289  }, null); // Action after fade in
290  }
291  }
292 
297  public void SetWorldPosition(Vector3 worldPosition)
298  {
299  // There are two things moving the camera: the camera parent (that this script is attached to)
300  // and the user's head (which the MR device is attached to. :)). When setting the world position,
301  // we need to set it relative to the user's head in the scene so they are looking/standing where
302  // we expect.
303  var newPosition = worldPosition - (CameraCache.Main.transform.position - transform.position);
304 
305  // If we're Stationary, we'll need to raycast to estimate our height. In RoomScale, that will be accounted for by the offset between the camera and its parent.
306 #if UNITY_2017_2_OR_NEWER
307  if (XRDevice.GetTrackingSpaceType() == TrackingSpaceType.Stationary && !StayOnTheFloor)
308 #else
309  if (VRDevice.GetTrackingSpaceType() == TrackingSpaceType.Stationary && !StayOnTheFloor)
310 #endif
311  {
312  RaycastHit hitInfo;
313  newPosition.y += (Physics.Raycast(CameraCache.Main.transform.position, Vector3.down, out hitInfo, 5.0f) ? hitInfo.distance : 1.7f);
314  }
315  else
316  {
317  newPosition.y = StayOnTheFloor ? transform.position.y : worldPosition.y;
318  }
319 
320  transform.position = newPosition;
321  }
322 
323  private void EnableMarker()
324  {
325  teleportMarker.SetActive(true);
326  if (animationController != null)
327  {
328  animationController.StartPlayback();
329  }
330  }
331 
332  private void DisableMarker()
333  {
334  if (animationController != null)
335  {
336  animationController.StopPlayback();
337  }
338  teleportMarker.SetActive(false);
339  }
340 
341  private void PositionMarker()
342  {
343  FocusDetails focusDetails = FocusManager.Instance.GetFocusDetails(currentPointingSource);
344 
345  if (focusDetails.Object != null && (Vector3.Dot(focusDetails.Normal, Vector3.up) > 0.90f))
346  {
347  isTeleportValid = true;
348 
349  teleportMarker.transform.position = focusDetails.Point;
350  }
351  else
352  {
353  isTeleportValid = false;
354  }
355 
356  animationController.speed = isTeleportValid ? 1 : 0;
357  }
358  }
359 }
bool DoFade(float _fadeOutTime, float _fadeInTime, Action _fadedOutAction, Action _fadedInAction)
Definition: FadeManager.cs:115
Script teleports the user to the location being gazed at when Y was pressed on a Gamepad.
uint SourceId
The id of the source the event is from, for instance the hand id.
Defines the controller mapping for the input source.
FocusDetails struct contains information about which game object has the focus currently. Also contains information about the normal of that point.
Definition: FocusDetails.cs:14
Vector2 Position
Two values, from -1.0 to 1.0 in the X-axis and Y-axis, representing where the input control is positi...
static string GetMapping(XboxControllerMappingTypes type)
Interface to implement to react to controller input changes.
static void AssertIsInitialized()
Definition: Singleton.cs:49
The purpose of this class is to provide a cached reference to the main camera. Calling Camera...
Definition: CameraCache.cs:12
static T Instance
Returns the Singleton instance of the classes type. If no instance is found, then we search for an in...
Definition: Singleton.cs:26
void OnInputPositionChanged(InputPositionEventData eventData)
static Camera Main
Returns a cached reference to the main camera and uses Camera.main if it hasn&#39;t been cached yet...
Definition: CameraCache.cs:20
Focus manager is the bridge that handles different types of pointing sources like gaze cursor or poin...
Definition: FocusManager.cs:16
InteractionSourcePressInfo PressType
Button type that initiated the event.
Implement this interface to register your pointer as a pointing source. This could be gaze based or m...
Utility class for Unity&#39;s Input Manager Mappings. Input from all types should be defined here for use...
XboxControllerMappingTypes
Xbox Controller axis and button types.
void SetWorldPosition(Vector3 worldPosition)
Places the player in the specified position of the world
Singleton behaviour class, used for components that should only have one instance.
Definition: Singleton.cs:14