EyeReticle.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.XR.Interaction.Toolkit;
  5. using UnityEngine.XR.Interaction.Toolkit.UI;
  6. using UnityEngine.EventSystems;
  7. namespace Ximmerse.XR.InputSystems.GazeAndGestureInteraction
  8. {
  9. /// <summary>
  10. /// Eye reticle - 注视点交互射线控制器。
  11. /// 需要场景中存在 XRUIInputModule 组件。
  12. /// </summary>
  13. [DisallowMultipleComponent]
  14. public class EyeReticle : XRRayInteractor
  15. {
  16. public class InteractingObjectDescription
  17. {
  18. public GameObject target;
  19. public bool isUI;
  20. }
  21. /// <summary>
  22. /// If true, eye reticle cusor texture is not displayed when interacting nothing.
  23. /// </summary>
  24. public bool DisplayEyeReticleCursorWhenIdle = true;
  25. ///// <summary>
  26. ///// The default reticle cursor texture.
  27. ///// </summary>
  28. //public Texture2D defaultReticleTexture;
  29. ///// <summary>
  30. ///// The reticle cursor texture when eye reticle hover interactable objects.
  31. ///// </summary>
  32. //public Texture2D hoverReticleTexture;
  33. ///// <summary>
  34. ///// The reticle cursor texture when eye reticle select interactable object.
  35. ///// </summary>
  36. //public Texture2D selectStateReticleTexture;
  37. XRUIInputModule m_CurrentXRInputModule;
  38. public enum EyeReticleTransformMode : byte
  39. {
  40. LockToHead = 0,
  41. LockToTarget,
  42. }
  43. /// <summary>
  44. /// The current transform mode of the eye reticle object.
  45. /// </summary>
  46. public EyeReticleTransformMode TransformMode
  47. {
  48. get; private set;
  49. }
  50. /// <summary>
  51. /// 当前 raycast hovering 目标对象
  52. /// </summary>
  53. public InteractingObjectDescription CurrentHoveringTarget
  54. {
  55. get; private set;
  56. }
  57. /// <summary>
  58. /// 当前交互中的目标对象
  59. /// </summary>
  60. public InteractingObjectDescription CurrentInteractingTarget
  61. {
  62. get; private set;
  63. }
  64. private GameObject target;
  65. //public GameObject plam;
  66. Vector3 targetaxis;
  67. private bool _select = false;
  68. float defaultRaycastingDistance = 0;
  69. protected override void Awake()
  70. {
  71. base.Awake();
  72. CurrentHoveringTarget = new InteractingObjectDescription();
  73. CurrentInteractingTarget = new InteractingObjectDescription();
  74. defaultRaycastingDistance = this.maxRaycastDistance;
  75. }
  76. protected override void OnEnable()
  77. {
  78. base.OnEnable();
  79. if (!m_CurrentXRInputModule)
  80. {
  81. m_CurrentXRInputModule = FindObjectOfType<XRUIInputModule>();
  82. }
  83. //UI:
  84. if (m_CurrentXRInputModule)
  85. {
  86. m_CurrentXRInputModule.pointerEnter += onXRUIInputPointerEnter;
  87. m_CurrentXRInputModule.pointerExit += onXRUIInputPointerExit;
  88. m_CurrentXRInputModule.pointerDown += onXRUIInputPointerDown;
  89. m_CurrentXRInputModule.pointerUp += onXRUIInputPointerUp;
  90. }
  91. //3D object:
  92. XimmerseXR.DisplayReticle = this.DisplayEyeReticleCursorWhenIdle;
  93. this.hoverEntered.AddListener(OnFirstHoverEntered);
  94. this.hoverExited.AddListener(OnLastHoverExited);
  95. this.selectEntered.AddListener(OnFirstSelectEntered);
  96. this.selectExited.AddListener(OnLastSelectExited);
  97. }
  98. protected override void OnDisable()
  99. {
  100. base.OnDisable();
  101. //UI:
  102. if (m_CurrentXRInputModule)
  103. {
  104. m_CurrentXRInputModule.pointerEnter -= onXRUIInputPointerEnter;
  105. m_CurrentXRInputModule.pointerExit -= onXRUIInputPointerExit;
  106. m_CurrentXRInputModule.pointerDown -= onXRUIInputPointerDown;
  107. m_CurrentXRInputModule.pointerUp -= onXRUIInputPointerUp;
  108. }
  109. //3D object:
  110. this.hoverEntered.RemoveListener(OnFirstHoverEntered);
  111. this.hoverExited.RemoveListener(OnLastHoverExited);
  112. this.selectEntered.RemoveListener(OnFirstSelectEntered);
  113. this.selectExited.RemoveListener(OnLastSelectExited);
  114. }
  115. #region Pointer Enter/Exit (UI), Hover Enter/Exit(3D Object)
  116. private void onXRUIInputPointerEnter(GameObject targetGameObject, PointerEventData pointerEvent)
  117. {
  118. Component interactionComponent = null;
  119. if (pointerEvent is TrackedDeviceEventData)
  120. {
  121. TrackedDeviceEventData tracEvntDta = pointerEvent as TrackedDeviceEventData;
  122. interactionComponent = tracEvntDta.interactor as Component;
  123. }
  124. if (ReferenceEquals(this, interactionComponent))
  125. {
  126. OnEyeRayEnterInternal(targetGameObject, true); //This event called when with UI object
  127. }
  128. // Debug.LogFormat("Raycaster: {0} enter: {1}", interactionComponent ? interactionComponent.name : "", targetGameObject.name);
  129. }
  130. private void onXRUIInputPointerExit(GameObject targetGameObject, PointerEventData pointerEvent)
  131. {
  132. Component interactionComponent = null;
  133. if (pointerEvent is TrackedDeviceEventData)
  134. {
  135. TrackedDeviceEventData tracEvntDta = pointerEvent as TrackedDeviceEventData;
  136. interactionComponent = tracEvntDta.interactor as Component;
  137. }
  138. if (ReferenceEquals(this, interactionComponent))
  139. {
  140. OnEyeRayExitInternal(targetGameObject, true); //This event called when with UI object
  141. }
  142. // Debug.LogFormat("Raycaster: {0} exit: {1}", interactionComponent ? interactionComponent.name : "", targetGameObject);
  143. }
  144. /// <summary>
  145. /// Hover enter : 显示reticle texture,示意此物体可以交互.
  146. /// 此方法在进入3D object的时候回调。等同于 UI 对象的 Pointer Enter
  147. /// </summary>
  148. /// <param name="args"></param>
  149. protected virtual void OnFirstHoverEntered(HoverEnterEventArgs args)
  150. {
  151. OnEyeRayEnterInternal(args.interactableObject.transform.gameObject, false); //This event called when with 3D object
  152. // Debug.LogFormat(args.interactorObject.transform, "On First Hover Enter: {0}", args.interactorObject.transform.name);
  153. }
  154. /// <summary>
  155. /// Hover exit : hide / switch reticle texture,示意已经离开交互区间
  156. /// 此方法在离开 3D object的时候回调。等同于 UI 对象的 Pointer Exit
  157. /// </summary>
  158. /// <param name="args"></param>
  159. protected virtual void OnLastHoverExited(HoverExitEventArgs args)
  160. {
  161. OnEyeRayExitInternal(args.interactableObject.transform.gameObject, false);
  162. // Debug.LogFormat(args.interactorObject.transform, "On Last Hover Enter: {0}", args.interactorObject.transform.name);
  163. }
  164. private void OnEyeRayEnterInternal(GameObject target, bool isUI)
  165. {
  166. this.CurrentHoveringTarget.target = target;
  167. this.CurrentHoveringTarget.isUI = isUI;
  168. }
  169. private void OnEyeRayExitInternal(GameObject target, bool isUI)
  170. {
  171. this.CurrentHoveringTarget.target = null;
  172. this.CurrentHoveringTarget.isUI = isUI;
  173. }
  174. #endregion
  175. #region Pointer Down/Up
  176. /// <summary>
  177. /// On GUI pointer down
  178. /// </summary>
  179. /// <param name="targetGameObject"></param>
  180. /// <param name="pointerEvent"></param>
  181. private void onXRUIInputPointerDown(GameObject targetGameObject, PointerEventData pointerEvent)
  182. {
  183. _select = true;
  184. Component interactionComponent = null;
  185. if (pointerEvent is TrackedDeviceEventData)
  186. {
  187. TrackedDeviceEventData tracEvntDta = pointerEvent as TrackedDeviceEventData;
  188. interactionComponent = tracEvntDta.interactor as Component;
  189. }
  190. if (ReferenceEquals(this, interactionComponent))
  191. {
  192. OnEyeReticlePointerDown(targetGameObject, true); //This event called when with UI object
  193. }
  194. }
  195. /// <summary>
  196. /// On GUI pointer up
  197. /// </summary>
  198. /// <param name="targetGameObject"></param>
  199. /// <param name="pointerEvent"></param>
  200. private void onXRUIInputPointerUp(GameObject targetGameObject, PointerEventData pointerEvent)
  201. {
  202. _select = false;
  203. Component interactionComponent = null;
  204. if (pointerEvent is TrackedDeviceEventData)
  205. {
  206. TrackedDeviceEventData tracEvntDta = pointerEvent as TrackedDeviceEventData;
  207. interactionComponent = tracEvntDta.interactor as Component;
  208. }
  209. if (ReferenceEquals(this, interactionComponent))
  210. {
  211. OnEyeReticlePointerUp(targetGameObject, true); //This event called when with UI object
  212. }
  213. }
  214. protected void OnFirstSelectEntered(SelectEnterEventArgs args)
  215. {
  216. Debug.Log("OnFirstSelectEntered");
  217. OnEyeReticlePointerDown(args.interactorObject.transform.gameObject, false);
  218. Debug.LogFormat(args.interactorObject.transform, "On First Select Enter: {0}", args.interactorObject.transform.name);
  219. }
  220. protected void OnLastSelectExited(SelectExitEventArgs args)
  221. {
  222. OnEyeReticlePointerUp(args.interactorObject.transform.gameObject, false);
  223. Debug.LogFormat(args.interactorObject.transform, "On Last Select Exit: {0}", args.interactorObject.transform.name);
  224. }
  225. bool isselect = true;
  226. protected override void OnSelectEntering(SelectEnterEventArgs args)
  227. {
  228. _select = true;
  229. base.OnSelectEntering(args);
  230. }
  231. protected override void OnSelectExited(SelectExitEventArgs args)
  232. {
  233. _select = false;
  234. base.OnSelectExited(args);
  235. }
  236. private bool _hover;
  237. protected override void OnHoverEntering(HoverEnterEventArgs args)
  238. {
  239. base.OnHoverEntering(args);
  240. }
  241. protected override void OnHoverExiting(HoverExitEventArgs args)
  242. {
  243. base.OnHoverExiting(args);
  244. }
  245. private void OnEyeReticlePointerDown(GameObject target, bool isUI)
  246. {
  247. _select = true;
  248. if (target && isUI)
  249. {
  250. this.CurrentInteractingTarget.target = target;
  251. this.CurrentInteractingTarget.isUI = isUI;
  252. if (isUI)
  253. {
  254. //SetTransformMode(EyeReticleTransformMode.LockToTarget);
  255. }
  256. Debug.LogFormat("On Eye Reticle Pointer down at UI :{0}", target);
  257. }
  258. }
  259. private void OnEyeReticlePointerUp(GameObject target, bool isUI)
  260. {
  261. _select = false;
  262. this.CurrentInteractingTarget.target = null;
  263. }
  264. #endregion
  265. //internal void SetTransformMode(EyeReticleTransformMode mode)
  266. //{
  267. // this.TransformMode = mode;
  268. // switch (mode)
  269. // {
  270. // case EyeReticleTransformMode.LockToHead:
  271. // //anchor to head transform:
  272. // transform.SetParent(Camera.main.transform, true);
  273. // transform.localPosition = Vector3.zero;
  274. // transform.localRotation = Quaternion.identity;
  275. // Debug.LogFormat("EyeReticle : lock to head !");
  276. // break;
  277. // case EyeReticleTransformMode.LockToTarget:
  278. // var targetTransform = CurrentInteractingTarget.target.transform;
  279. // if (targetTransform)
  280. // {
  281. // transform.position = targetTransform.position + -targetTransform.forward;
  282. // transform.forward = targetTransform.forward;
  283. // transform.SetParent(targetTransform, true);
  284. // Debug.LogFormat("EyeReticle : lock to target UI !");
  285. // }
  286. // else
  287. // {
  288. // //unsupport
  289. // }
  290. // break;
  291. // }
  292. //}
  293. private void LateUpdate()
  294. {
  295. var currentHandGestureTrackingState = GazeAndHandInteractionSystem.instance.TrackingState;
  296. //无手势数据的时候,清空交互对象引用:
  297. if (!currentHandGestureTrackingState.IsTracking || CurrentInteractingTarget.target)
  298. {
  299. CurrentInteractingTarget.target = null;
  300. }
  301. if (HandTracking.HandTrackingInfo.IsTracking)
  302. {
  303. if (_select)
  304. {
  305. SelectState();
  306. }
  307. else if (this.CurrentHoveringTarget.target)
  308. {
  309. HoverState();
  310. }
  311. else
  312. {
  313. TrackingState();
  314. }
  315. }
  316. else
  317. {
  318. if (this.CurrentHoveringTarget.target)
  319. {
  320. CursorManager.Instance.HideCursor();
  321. CursorManager.Instance.NormalReticle();
  322. }
  323. else
  324. {
  325. NormalState();
  326. }
  327. }
  328. XimmerseXR.DisplayReticle = true;
  329. }
  330. #region cursorstate
  331. private void TrackingState()
  332. {
  333. CursorManager.Instance.TrackingCursor();
  334. }
  335. private void NormalState()
  336. {
  337. CursorManager.Instance.NormalCursor();
  338. //CursorManager.Instance.HideReticle();
  339. }
  340. private void SelectState()
  341. {
  342. CursorManager.Instance.HideCursor();
  343. CursorManager.Instance.HideReticle();
  344. }
  345. private void HoverState()
  346. {
  347. CursorManager.Instance.HideCursor();
  348. CursorManager.Instance.TrackingReticle();
  349. }
  350. #endregion
  351. }
  352. }