AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
FocusManager.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 System.Collections.Generic;
6 using UnityEngine;
7 using UnityEngine.EventSystems;
8 
9 namespace HoloToolkit.Unity.InputModule
10 {
16  public class FocusManager : Singleton<FocusManager>
17  {
18  #region MonoBehaviour Implementation
19 
20  protected override void Awake()
21  {
22  base.Awake();
23 
24  if (registeredPointers != null)
25  {
26  for (int iPointer = 0; iPointer < registeredPointers.Length; iPointer++)
27  {
28  GameObject owner = registeredPointers[iPointer];
29 
30  if (owner == null)
31  {
32  Debug.LogError("AutoRegisteredPointers contains a null (\"None\") object.");
33  break;
34  }
35 
36  IPointingSource pointingSource = owner.GetComponent<IPointingSource>();
37 
38  if (pointingSource == null)
39  {
40  Debug.LogErrorFormat("AutoRegisteredPointers contains object \"{0}\" which is missing its {1} component.",
41  owner.name,
42  typeof(IPointingSource).Name
43  );
44  break;
45  }
46 
47  RegisterPointer(pointingSource);
48  }
49  }
50  }
51 
52  private void Start()
53  {
54  if (pointers.Count == 0 && autoRegisterGazePointerIfNoPointersRegistered && GazeManager.IsInitialized)
55  {
56  RegisterPointer(GazeManager.Instance);
57  }
58  }
59 
60  private void Update()
61  {
62  UpdatePointers();
63  UpdateFocusedObjects();
64  }
65 
66  #endregion
67 
68  #region Settings
69 
73  [SerializeField]
74  private float pointingExtent = 10f;
75 
76  public float GlobalPointingExtent { get { return pointingExtent; } }
77 
89  [SerializeField]
90  [Tooltip("The LayerMasks, in prioritized order, that are used to determine the HitObject when raycasting.")]
91  private LayerMask[] pointingRaycastLayerMasks = { Physics.DefaultRaycastLayers };
92 
93  [SerializeField]
94  private GameObject[] registeredPointers = null;
95 
96  [SerializeField]
97  private bool autoRegisterGazePointerIfNoPointersRegistered = true;
98 
99  [SerializeField]
100  private bool debugDrawPointingRays = false;
101 
102  [SerializeField]
103  private Color[] debugDrawPointingRayColors = null;
104 
105  #endregion
106 
107  #region Data
108 
109  private class PointerData : PointerResult
110  {
111  public readonly IPointingSource PointingSource;
112 
113  private PointerInputEventData pointerData;
114  public PointerInputEventData UnityUIPointerData
115  {
116  get
117  {
118  if (pointerData == null)
119  {
120  pointerData = new PointerInputEventData(EventSystem.current);
121  }
122 
123  return pointerData;
124  }
125  }
126 
127  public PointerData(IPointingSource pointingSource)
128  {
129  PointingSource = pointingSource;
130  }
131 
132  [Obsolete("Use UpdateHit(RaycastHit hit, RayStep sourceRay, int rayStepIndex) or UpdateHit (float extent)")]
133  public void UpdateHit(RaycastHit hit)
134  {
135  throw new NotImplementedException();
136  }
137 
138  public void UpdateHit(RaycastHit hit, RayStep sourceRay, int rayStepIndex)
139  {
140  LastRaycastHit = hit;
141  PreviousEndObject = End.Object;
142  RayStepIndex = rayStepIndex;
143 
144  StartPoint = sourceRay.Origin;
145  End = new FocusDetails
146  {
147  Point = hit.point,
148  Normal = hit.normal,
149  Object = hit.transform.gameObject
150  };
151  }
152 
153  public void UpdateHit(RaycastResult result, RaycastHit hit, RayStep sourceRay, int rayStepIndex)
154  {
155  // We do not update the PreviousEndObject here because
156  // it's already been updated in the first physics raycast.
157 
158  RayStepIndex = rayStepIndex;
159  StartPoint = sourceRay.Origin;
160  End = new FocusDetails
161  {
162  Point = hit.point,
163  Normal = hit.normal,
164  Object = result.gameObject
165  };
166  }
167 
168  public void UpdateHit(float extent)
169  {
170  PreviousEndObject = End.Object;
171 
172  RayStep firstStep = PointingSource.Rays[0];
173  RayStep finalStep = PointingSource.Rays[PointingSource.Rays.Length - 1];
174  RayStepIndex = 0;
175 
176  StartPoint = firstStep.Origin;
177  End = new FocusDetails
178  {
179  Point = finalStep.Terminus,
180  Normal = (-finalStep.Direction),
181  Object = null
182  };
183  }
184 
185  public void ResetFocusedObjects(bool clearPreviousObject = true)
186  {
187  if (clearPreviousObject)
188  {
189  PreviousEndObject = null;
190  }
191 
192  End = new FocusDetails
193  {
194  Point = End.Point,
195  Normal = End.Normal,
196  Object = null
197  };
198  }
199  }
200 
201  private readonly List<PointerData> pointers = new List<PointerData>(0);
202 
208  private PointerData gazeManagerPointingData;
209 
210  [Obsolete("Use GetGazePointerEventData or GetSpecificPointerEventData")]
211  public PointerInputEventData UnityUIPointerEvent { get; private set; }
212 
213  private readonly HashSet<GameObject> pendingOverallFocusEnterSet = new HashSet<GameObject>();
214  private readonly HashSet<GameObject> pendingOverallFocusExitSet = new HashSet<GameObject>();
215  private readonly List<PointerData> pendingPointerSpecificFocusChange = new List<PointerData>();
216 
221  private Vector3 newUiRaycastPosition = Vector3.zero;
222 
226  [SerializeField]
227  private Camera uiRaycastCamera;
228 
233  public Camera UIRaycastCamera
234  {
235  get
236  {
237  if (uiRaycastCamera == null)
238  {
239  Debug.LogWarning("No UIRaycastCamera assigned! Falling back to the RaycastCamera.\n" +
240  "It's highly recommended to use the RaycastCamera found on the EventSystem of this InputManager.");
241  uiRaycastCamera = GetComponentInChildren<Camera>();
242  }
243 
244  return uiRaycastCamera;
245  }
246  }
247 
248  #endregion
249 
250  #region Accessors
251 
252  public void RegisterPointer(IPointingSource pointingSource)
253  {
254  Debug.Assert(pointingSource != null, "Can't register a pointer if you give us one.");
255 
256  int pointerIndex;
257  PointerData pointer;
258 
259  if (TryGetPointerIndex(pointingSource, out pointerIndex))
260  {
261  // This pointing source is already registered and active.
262  return;
263  }
264 
265  if (pointingSource is GazeManager)
266  {
267  if (gazeManagerPointingData == null)
268  {
269  if (GazeManager.IsInitialized)
270  {
271  gazeManagerPointingData = new PointerData(GazeManager.Instance);
272  }
273  }
274  else
275  {
276  Debug.Assert(ReferenceEquals(gazeManagerPointingData.PointingSource, GazeManager.Instance));
277  gazeManagerPointingData.ResetFocusedObjects();
278  }
279 
280  Debug.Assert(gazeManagerPointingData != null);
281  pointer = gazeManagerPointingData;
282  }
283  else
284  {
285  pointer = new PointerData(pointingSource);
286  }
287 
288  pointers.Add(pointer);
289  }
290 
291  public void UnregisterPointer(IPointingSource pointingSource)
292  {
293  Debug.Assert(pointingSource != null, "Can't unregister a pointer if you give us one.");
294 
295  int pointerIndex;
296  TryGetPointerIndex(pointingSource, out pointerIndex);
297  Debug.Assert(pointerIndex >= 0, "Invalid pointer index!");
298 
299  PointerData pointer;
300  GetPointerData(pointingSource, out pointer);
301  Debug.Assert(pointer != null, "Attempting to unregister a pointer that was never registered!");
302 
303  // Should we be protecting against unregistering the GazeManager?
304 
305  pointers.RemoveAt(pointerIndex);
306 
307  // Raise focus events if needed:
308 
309  if (pointer.End.Object != null)
310  {
311  GameObject unfocusedObject = pointer.End.Object;
312 
313  bool objectIsStillFocusedByOtherPointer = false;
314 
315  for (int iOther = 0; iOther < pointers.Count; iOther++)
316  {
317  if (pointers[iOther].End.Object == unfocusedObject)
318  {
319  objectIsStillFocusedByOtherPointer = true;
320  break;
321  }
322  }
323 
324  if (!objectIsStillFocusedByOtherPointer)
325  {
326  RaiseFocusExitedEvents(unfocusedObject);
327  }
328 
329  RaisePointerSpecificFocusChangedEvents(pointer.PointingSource, unfocusedObject, null);
330  }
331  }
332 
333  public FocusDetails? TryGetFocusDetails(BaseEventData eventData)
334  {
335  for (int i = 0; i < pointers.Count; i++)
336  {
337  if (pointers[i].PointingSource.OwnsInput(eventData))
338  {
339  return pointers[i].End;
340  }
341  }
342 
343  return null;
344  }
345 
346  public GameObject TryGetFocusedObject(BaseEventData eventData)
347  {
348  IPointingSource pointingSource;
349  TryGetPointingSource(eventData, out pointingSource);
350  PointerInputEventData pointerInputEventData = GetSpecificPointerEventData(pointingSource);
351 
352  // GetSpecificPointerEventData can return null. Be sure to handle that case.
353  if (pointerInputEventData == null)
354  {
355  return null;
356  }
357  return pointerInputEventData.selectedObject;
358  }
359 
360  public bool TryGetPointingSource(BaseEventData eventData, out IPointingSource pointingSource)
361  {
362  // pre-initialize pointingSource to null, assuming we will return false
363  pointingSource = null;
364  if (eventData == null) { return false; }
365 
366  for (int i = 0; i < pointers.Count; i++)
367  {
368  if (pointers[i].PointingSource.OwnsInput(eventData))
369  {
370  pointingSource = pointers[i].PointingSource;
371  return true;
372  }
373  }
374 
375  return false;
376  }
377 
379  {
380  PointerData pointerData;
381  FocusDetails details = default(FocusDetails);
382 
383  if (GetPointerData(pointingSource, out pointerData))
384  {
385  details = pointerData.End;
386  }
387 
388  return details;
389  }
390 
391  public GameObject GetFocusedObject(IPointingSource pointingSource)
392  {
393  PointerData pointerData;
394  GameObject focusedObject = null;
395 
396  if (GetPointerData(pointingSource, out pointerData))
397  {
398  focusedObject = pointerData.End.Object;
399  }
400 
401  return focusedObject;
402  }
403 
408  public bool TryGetSinglePointer(out IPointingSource pointingSource)
409  {
410  if (pointers.Count == 1)
411  {
412  pointingSource = pointers[0].PointingSource;
413  return true;
414  }
415 
416  pointingSource = null;
417  return false;
418  }
419 
420  public delegate void FocusEnteredMethod(GameObject focusedObject);
421  public event FocusEnteredMethod FocusEntered;
422 
423  public delegate void FocusExitedMethod(GameObject unfocusedObject);
424  public event FocusExitedMethod FocusExited;
425 
426  public delegate void PointerSpecificFocusChangedMethod(IPointingSource pointer, GameObject oldFocusedObject, GameObject newFocusedObject);
427  public event PointerSpecificFocusChangedMethod PointerSpecificFocusChanged;
428 
429  [Obsolete("Use either GetGazePointerEventData or GetSpecificPointerEventData")]
431  {
432  return GetGazePointerEventData();
433  }
434 
436  {
437  return gazeManagerPointingData.UnityUIPointerData;
438  }
439 
441  {
442  PointerData pointerEventData;
443 
444  if (!GetPointerData(pointer, out pointerEventData)) { return null; }
445 
446  pointerEventData.UnityUIPointerData.selectedObject = GetFocusedObject(pointer);
447  return pointerEventData.UnityUIPointerData;
448  }
449 
450  public float GetPointingExtent(IPointingSource pointingSource)
451  {
452  return pointingSource.ExtentOverride ?? pointingExtent;
453  }
454 
455  #endregion
456 
457  #region Utilities
458 
459  private void UpdatePointers()
460  {
461  bool gazeManagerIsRegistered = false;
462 
463  for (int iPointer = 0; iPointer < pointers.Count; iPointer++)
464  {
465  PointerData pointer = pointers[iPointer];
466 
467  if (pointer == gazeManagerPointingData)
468  {
469  gazeManagerIsRegistered = true;
470  }
471 
472  UpdatePointer(pointer);
473 
474  if (debugDrawPointingRays)
475  {
476  Color rayColor;
477 
478  if ((debugDrawPointingRayColors != null) && (debugDrawPointingRayColors.Length > 0))
479  {
480  rayColor = debugDrawPointingRayColors[iPointer % debugDrawPointingRayColors.Length];
481  }
482  else
483  {
484  rayColor = Color.green;
485  }
486 
487  Debug.DrawRay(pointer.StartPoint, (pointer.End.Point - pointer.StartPoint), rayColor);
488  }
489  }
490 
491  if (gazeManagerPointingData != null)
492  {
493  Debug.Assert(ReferenceEquals(gazeManagerPointingData.PointingSource, GazeManager.Instance));
494 
495  if (!gazeManagerIsRegistered)
496  {
497  UpdatePointer(gazeManagerPointingData);
498  }
499 
500  GazeManager.Instance.UpdateHitDetails(gazeManagerPointingData.End, gazeManagerPointingData.LastRaycastHit, gazeManagerIsRegistered);
501  }
502  }
503 
504  private void UpdatePointer(PointerData pointer)
505  {
506  // Call the pointer's OnPreRaycast function
507  // This will give it a chance to prepare itself for raycasts
508  // eg, by building its Rays array
509  pointer.PointingSource.OnPreRaycast();
510 
511  // If pointer interaction isn't enabled, clear its result object and return
512  if (!pointer.PointingSource.InteractionEnabled)
513  {
514  // Don't clear the previous focused object since we still want to trigger FocusExit events
515  pointer.ResetFocusedObjects(false);
516  }
517  else
518  {
519  // If the pointer is locked
520  // Keep the focus objects the same
521  // This will ensure that we execute events on those objects
522  // even if the pointer isn't pointing at them
523  if (!pointer.PointingSource.FocusLocked)
524  {
525  // Otherwise, continue
526  var prioritizedLayerMasks = (pointer.PointingSource.PrioritizedLayerMasksOverride ?? pointingRaycastLayerMasks);
527 
528  // Perform raycast to determine focused object
529  RaycastPhysics(pointer, prioritizedLayerMasks);
530 
531  // If we have a unity event system, perform graphics raycasts as well to support Unity UI interactions
532  if (EventSystem.current != null)
533  {
534  // NOTE: We need to do this AFTER RaycastPhysics so we use the current hit point to perform the correct 2D UI Raycast.
535  RaycastUnityUI(pointer, prioritizedLayerMasks);
536  }
537 
538  // Set the pointer's result last
539  pointer.PointingSource.Result = pointer;
540  }
541  }
542 
543  // Call the pointer's OnPostRaycast function
544  // This will give it a chance to respond to raycast results
545  // eg by updating its appearance
546  pointer.PointingSource.OnPostRaycast();
547  }
548 
552  private void RaycastPhysics(PointerData pointer, LayerMask[] prioritizedLayerMasks)
553  {
554  bool isHit = false;
555  int rayStepIndex = 0;
556  RayStep rayStep = default(RayStep);
557  RaycastHit physicsHit = default(RaycastHit);
558 
559  // Comment back in GetType() only when debugging for a specific pointer.
560  Debug.Assert(pointer.PointingSource.Rays != null, "No valid rays for pointer "/* + pointer.GetType()*/);
561  Debug.Assert(pointer.PointingSource.Rays.Length > 0, "No valid rays for pointer "/* + pointer.GetType()*/);
562 
563  // Check raycast for each step in the pointing source
564  for (int i = 0; i < pointer.PointingSource.Rays.Length; i++)
565  {
566  if (RaycastPhysicsStep(pointer.PointingSource.Rays[i], prioritizedLayerMasks, out physicsHit))
567  {
568  // Set the pointer source's origin ray to this step
569  isHit = true;
570  rayStep = pointer.PointingSource.Rays[i];
571  rayStepIndex = i;
572  // No need to continue once we've hit something
573  break;
574  }
575  }
576 
577  if (isHit)
578  {
579  pointer.UpdateHit(physicsHit, rayStep, rayStepIndex);
580  }
581  else
582  {
583  pointer.UpdateHit(GetPointingExtent(pointer.PointingSource));
584  }
585  }
586 
587  private bool RaycastPhysicsStep(RayStep step, LayerMask[] prioritizedLayerMasks, out RaycastHit physicsHit)
588  {
589  bool isHit = false;
590  physicsHit = default(RaycastHit);
591 
592  // If there is only one priority, don't prioritize
593  if (prioritizedLayerMasks.Length == 1)
594  {
595  isHit = Physics.Raycast(step.Origin, step.Direction, out physicsHit, step.Length, prioritizedLayerMasks[0]);
596  }
597  else
598  {
599  // Raycast across all layers and prioritize
600  RaycastHit? hit = PrioritizeHits(Physics.RaycastAll(step.Origin, step.Direction, step.Length, Physics.AllLayers), prioritizedLayerMasks);
601  isHit = hit.HasValue;
602 
603  if (isHit)
604  {
605  physicsHit = hit.Value;
606  }
607  }
608 
609  return isHit;
610  }
611 
612  private void RaycastUnityUI(PointerData pointer, LayerMask[] prioritizedLayerMasks)
613  {
614  Debug.Assert(UIRaycastCamera != null, "You must assign a UIRaycastCamera on the FocusManager before you can process uGUI raycasting.");
615 
616  RaycastResult uiRaycastResult = default(RaycastResult);
617  bool overridePhysicsRaycast = false;
618  RayStep rayStep = default(RayStep);
619  int rayStepIndex = 0;
620 
621  // Comment back in GetType() only when debugging for a specific pointer.
622  Debug.Assert(pointer.PointingSource.Rays != null, "No valid rays for pointer "/* + pointer.GetType()*/);
623  Debug.Assert(pointer.PointingSource.Rays.Length > 0, "No valid rays for pointer "/* + pointer.GetType()*/);
624 
625  // Cast rays for every step until we score a hit
626  for (int i = 0; i < pointer.PointingSource.Rays.Length; i++)
627  {
628  if (RaycastUnityUIStep(pointer, pointer.PointingSource.Rays[i], prioritizedLayerMasks, out overridePhysicsRaycast, out uiRaycastResult))
629  {
630  rayStepIndex = i;
631  rayStep = pointer.PointingSource.Rays[i];
632  break;
633  }
634  }
635 
636  // Check if we need to overwrite the physics raycast info
637  if ((pointer.End.Object == null || overridePhysicsRaycast) && uiRaycastResult.isValid &&
638  uiRaycastResult.module != null && uiRaycastResult.module.eventCamera == UIRaycastCamera)
639  {
640  newUiRaycastPosition.x = uiRaycastResult.screenPosition.x;
641  newUiRaycastPosition.y = uiRaycastResult.screenPosition.y;
642  newUiRaycastPosition.z = uiRaycastResult.distance;
643 
644  Vector3 worldPos = UIRaycastCamera.ScreenToWorldPoint(newUiRaycastPosition);
645 
646  var hitInfo = new RaycastHit
647  {
648  point = worldPos,
649  normal = -uiRaycastResult.gameObject.transform.forward
650  };
651 
652  pointer.UpdateHit(uiRaycastResult, hitInfo, rayStep, rayStepIndex);
653  }
654  }
655 
656  private bool RaycastUnityUIStep(PointerData pointer, RayStep step, LayerMask[] prioritizedLayerMasks, out bool overridePhysicsRaycast, out RaycastResult uiRaycastResult)
657  {
658  // Move the uiRaycast camera to the current pointer's position.
659  UIRaycastCamera.transform.position = step.Origin;
660  UIRaycastCamera.transform.forward = step.Direction;
661 
662  // We always raycast from the center of the camera.
663  pointer.UnityUIPointerData.position = new Vector2(UIRaycastCamera.pixelWidth * 0.5f, UIRaycastCamera.pixelHeight * 0.5f);
664 
665  // Graphics raycast
666  uiRaycastResult = EventSystem.current.Raycast(pointer.UnityUIPointerData, prioritizedLayerMasks);
667  pointer.UnityUIPointerData.pointerCurrentRaycast = uiRaycastResult;
668 
669  overridePhysicsRaycast = false;
670 
671  // If we have a raycast result, check if we need to overwrite the physics raycast info
672  if (uiRaycastResult.gameObject != null)
673  {
674  if (pointer.End.Object != null)
675  {
676  // Check layer prioritization
677  if (prioritizedLayerMasks.Length > 1)
678  {
679  // Get the index in the prioritized layer masks
680  int uiLayerIndex = uiRaycastResult.gameObject.layer.FindLayerListIndex(prioritizedLayerMasks);
681  int threeDLayerIndex = pointer.LastRaycastHit.collider.gameObject.layer.FindLayerListIndex(prioritizedLayerMasks);
682 
683  if (threeDLayerIndex > uiLayerIndex)
684  {
685  overridePhysicsRaycast = true;
686  }
687  else if (threeDLayerIndex == uiLayerIndex)
688  {
689  if (pointer.LastRaycastHit.distance > uiRaycastResult.distance)
690  {
691  overridePhysicsRaycast = true;
692  }
693  }
694  }
695  else
696  {
697  if (pointer.LastRaycastHit.distance > uiRaycastResult.distance)
698  {
699  overridePhysicsRaycast = true;
700  }
701  }
702  }
703  // If we've hit something, no need to go further
704  return true;
705  }
706  // If we haven't hit something, keep going
707  return false;
708  }
709 
710  private void UpdateFocusedObjects()
711  {
712  Debug.Assert(pendingPointerSpecificFocusChange.Count == 0);
713  Debug.Assert(pendingOverallFocusExitSet.Count == 0);
714  Debug.Assert(pendingOverallFocusEnterSet.Count == 0);
715 
716  // NOTE: We compute the set of events to send before sending the first event
717  // just in case someone responds to the event by adding/removing a
718  // pointer which would change the structures we're iterating over.
719 
720  for (int iPointer = 0; iPointer < pointers.Count; iPointer++)
721  {
722  PointerData pointer = pointers[iPointer];
723 
724  if (pointer.PreviousEndObject != pointer.End.Object)
725  {
726  pendingPointerSpecificFocusChange.Add(pointer);
727 
728  // Initially, we assume all pointer-specific focus changes will result
729  // also result in an overall focus change...
730 
731  if (pointer.PreviousEndObject != null)
732  {
733  pendingOverallFocusExitSet.Add(pointer.PreviousEndObject);
734  }
735 
736  if (pointer.End.Object != null)
737  {
738  pendingOverallFocusEnterSet.Add(pointer.End.Object);
739  }
740  }
741  }
742 
743  // ... but now we trim out objects whose overall focus was maintained the same by a different pointer:
744 
745  for (int iPointer = 0; iPointer < pointers.Count; iPointer++)
746  {
747  PointerData pointer = pointers[iPointer];
748 
749  pendingOverallFocusExitSet.Remove(pointer.End.Object);
750 
751  pendingOverallFocusEnterSet.Remove(pointer.PreviousEndObject);
752  }
753 
754  // Now we raise the events:
755 
756  foreach (GameObject exit in pendingOverallFocusExitSet)
757  {
758  RaiseFocusExitedEvents(exit);
759  }
760 
761  foreach (GameObject enter in pendingOverallFocusEnterSet)
762  {
763  RaiseFocusEnteredEvents(enter);
764  }
765 
766  for (int iChange = 0; iChange < pendingPointerSpecificFocusChange.Count; iChange++)
767  {
768  PointerData change = pendingPointerSpecificFocusChange[iChange];
769 
770  RaisePointerSpecificFocusChangedEvents(change.PointingSource, change.PreviousEndObject, change.End.Object);
771  }
772 
773  pendingOverallFocusEnterSet.Clear();
774  pendingOverallFocusExitSet.Clear();
775  pendingPointerSpecificFocusChange.Clear();
776  }
777 
778  private void RaiseFocusExitedEvents(GameObject unfocusedObject)
779  {
780  InputManager.Instance.RaiseFocusExit(unfocusedObject);
781  //Debug.Log("Focus Exit: " + unfocusedObject.name);
782  if (FocusExited != null)
783  {
784  FocusExited(unfocusedObject);
785  }
786  }
787 
788  private void RaiseFocusEnteredEvents(GameObject focusedObject)
789  {
790  InputManager.Instance.RaiseFocusEnter(focusedObject);
791  //Debug.Log("Focus Enter: " + focusedObject.name);
792  if (FocusEntered != null)
793  {
794  FocusEntered(focusedObject);
795  }
796  }
797 
798  private void RaisePointerSpecificFocusChangedEvents(IPointingSource pointer, GameObject oldFocusedObject, GameObject newFocusedObject)
799  {
800  InputManager.Instance.RaisePointerSpecificFocusChangedEvents(pointer, oldFocusedObject, newFocusedObject);
801 
802  if (PointerSpecificFocusChanged != null)
803  {
804  PointerSpecificFocusChanged(pointer, oldFocusedObject, newFocusedObject);
805  }
806  }
807 
808  private bool GetPointerData(IPointingSource pointingSource, out PointerData pointerData)
809  {
810  int pointerIndex;
811 
812  if (TryGetPointerIndex(pointingSource, out pointerIndex))
813  {
814  pointerData = pointers[pointerIndex];
815  return true;
816  }
817 
818  pointerData = null;
819  return false;
820  }
821 
822  private bool TryGetPointerIndex(IPointingSource pointingSource, out int pointerIndex)
823  {
824  for (int i = 0; i < pointers.Count; i++)
825  {
826  if (pointingSource == pointers[i].PointingSource)
827  {
828  pointerIndex = i;
829  return true;
830  }
831  }
832 
833  pointerIndex = -1;
834  return false;
835  }
836 
837  private RaycastHit? PrioritizeHits(RaycastHit[] hits, LayerMask[] layerMasks)
838  {
839  if (hits.Length == 0)
840  {
841  return null;
842  }
843 
844  // Return the minimum distance hit within the first layer that has hits.
845  // In other words, sort all hit objects first by layerMask, then by distance.
846  for (int layerMaskIdx = 0; layerMaskIdx < layerMasks.Length; layerMaskIdx++)
847  {
848  RaycastHit? minHit = null;
849 
850  for (int hitIdx = 0; hitIdx < hits.Length; hitIdx++)
851  {
852  RaycastHit hit = hits[hitIdx];
853  if (hit.transform.gameObject.layer.IsInLayerMask(layerMasks[layerMaskIdx]) &&
854  (minHit == null || hit.distance < minHit.Value.distance))
855  {
856  minHit = hit;
857  }
858  }
859 
860  if (minHit != null)
861  {
862  return minHit;
863  }
864  }
865 
866  return null;
867  }
868 
874  {
875  Debug.Assert(UIRaycastCamera != null, "You must assign a UIRaycastCamera on the FocusManager before updating your canvases.");
876 
877  // This will also find disabled GameObjects in the scene.
878  // Warning! this look up is very expensive!
879  var sceneCanvases = Resources.FindObjectsOfTypeAll<Canvas>();
880 
881  for (var i = 0; i < sceneCanvases.Length; i++)
882  {
883  if (sceneCanvases[i].isRootCanvas && sceneCanvases[i].renderMode == RenderMode.WorldSpace)
884  {
885  sceneCanvases[i].worldCamera = UIRaycastCamera;
886  }
887  }
888  }
889 
890  #endregion
891  }
892 }
The gaze manager manages everything related to a gaze ray that can interact with other objects...
Definition: GazeManager.cs:13
override void Awake()
Base Awake method that sets the Singleton&#39;s unique instance. Called by Unity when initializing a Mono...
Definition: FocusManager.cs:20
GameObject TryGetFocusedObject(BaseEventData eventData)
Describes a Unity pointer event that was generated by a specific input source and ID...
Input Manager is responsible for managing input sources and dispatching relevant events to the approp...
Definition: InputManager.cs:19
void UnregisterPointer(IPointingSource pointingSource)
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
PointerInputEventData GetGazePointerEventData()
FocusDetails TryGetFocusDetails(BaseEventData eventData)
GameObject GetFocusedObject(IPointingSource pointingSource)
bool TryGetPointingSource(BaseEventData eventData, out IPointingSource pointingSource)
bool TryGetSinglePointer(out IPointingSource pointingSource)
Checks if exactly one pointer is registered and returns it if so.
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
FocusDetails GetFocusDetails(IPointingSource pointingSource)
PointerInputEventData GetSpecificPointerEventData(IPointingSource pointer)
PointerSpecificFocusChangedMethod PointerSpecificFocusChanged
Focus manager is the bridge that handles different types of pointing sources like gaze cursor or poin...
Definition: FocusManager.cs:16
Implement this interface to register your pointer as a pointing source. This could be gaze based or m...
PointerInputEventData GetPointerEventData()
void UpdateCanvasEventSystems()
Helper for assigning world space canvases event cameras.
void RegisterPointer(IPointingSource pointingSource)
static bool IsInitialized
Returns whether the instance has been initialized or not.
Definition: Singleton.cs:58
float GetPointingExtent(IPointingSource pointingSource)
Singleton behaviour class, used for components that should only have one instance.
Definition: Singleton.cs:14