暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

EventSystem.cs 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using UnityEngine;
  5. using UnityEngine.Rendering;
  6. using UnityEngine.Serialization;
  7. using UnityEngine.UIElements;
  8. namespace UnityEngine.EventSystems
  9. {
  10. [AddComponentMenu("Event/Event System")]
  11. [DisallowMultipleComponent]
  12. /// <summary>
  13. /// Handles input, raycasting, and sending events.
  14. /// </summary>
  15. /// <remarks>
  16. /// The EventSystem is responsible for processing and handling events in a Unity scene. A scene should only contain one EventSystem. The EventSystem works in conjunction with a number of modules and mostly just holds state and delegates functionality to specific, overrideable components.
  17. /// When the EventSystem is started it searches for any BaseInputModules attached to the same GameObject and adds them to an internal list. On update each attached module receives an UpdateModules call, where the module can modify internal state. After each module has been Updated the active module has the Process call executed.This is where custom module processing can take place.
  18. /// </remarks>
  19. public class EventSystem : UIBehaviour
  20. {
  21. private List<BaseInputModule> m_SystemInputModules = new List<BaseInputModule>();
  22. private BaseInputModule m_CurrentInputModule;
  23. private static List<EventSystem> m_EventSystems = new List<EventSystem>();
  24. /// <summary>
  25. /// Return the current EventSystem.
  26. /// </summary>
  27. public static EventSystem current
  28. {
  29. get { return m_EventSystems.Count > 0 ? m_EventSystems[0] : null; }
  30. set
  31. {
  32. int index = m_EventSystems.IndexOf(value);
  33. if (index > 0)
  34. {
  35. m_EventSystems.RemoveAt(index);
  36. m_EventSystems.Insert(0, value);
  37. }
  38. else if (index < 0)
  39. {
  40. Debug.LogError("Failed setting EventSystem.current to unknown EventSystem " + value);
  41. }
  42. }
  43. }
  44. [SerializeField]
  45. [FormerlySerializedAs("m_Selected")]
  46. private GameObject m_FirstSelected;
  47. [SerializeField]
  48. private bool m_sendNavigationEvents = true;
  49. /// <summary>
  50. /// Should the EventSystem allow navigation events (move / submit / cancel).
  51. /// </summary>
  52. public bool sendNavigationEvents
  53. {
  54. get { return m_sendNavigationEvents; }
  55. set { m_sendNavigationEvents = value; }
  56. }
  57. [SerializeField]
  58. private int m_DragThreshold = 10;
  59. /// <summary>
  60. /// The soft area for dragging in pixels.
  61. /// </summary>
  62. public int pixelDragThreshold
  63. {
  64. get { return m_DragThreshold; }
  65. set { m_DragThreshold = value; }
  66. }
  67. private GameObject m_CurrentSelected;
  68. /// <summary>
  69. /// The currently active EventSystems.BaseInputModule.
  70. /// </summary>
  71. public BaseInputModule currentInputModule
  72. {
  73. get { return m_CurrentInputModule; }
  74. }
  75. /// <summary>
  76. /// Only one object can be selected at a time. Think: controller-selected button.
  77. /// </summary>
  78. public GameObject firstSelectedGameObject
  79. {
  80. get { return m_FirstSelected; }
  81. set { m_FirstSelected = value; }
  82. }
  83. /// <summary>
  84. /// The GameObject currently considered active by the EventSystem.
  85. /// </summary>
  86. public GameObject currentSelectedGameObject
  87. {
  88. get { return m_CurrentSelected; }
  89. }
  90. [Obsolete("lastSelectedGameObject is no longer supported")]
  91. public GameObject lastSelectedGameObject
  92. {
  93. get { return null; }
  94. }
  95. private bool m_HasFocus = true;
  96. /// <summary>
  97. /// Flag to say whether the EventSystem thinks it should be paused or not based upon focused state.
  98. /// </summary>
  99. /// <remarks>
  100. /// Used to determine inside the individual InputModules if the module should be ticked while the application doesnt have focus.
  101. /// </remarks>
  102. public bool isFocused
  103. {
  104. get { return m_HasFocus; }
  105. }
  106. protected EventSystem()
  107. {}
  108. /// <summary>
  109. /// Recalculate the internal list of BaseInputModules.
  110. /// </summary>
  111. public void UpdateModules()
  112. {
  113. GetComponents(m_SystemInputModules);
  114. var systemInputModulesCount = m_SystemInputModules.Count;
  115. for (int i = systemInputModulesCount - 1; i >= 0; i--)
  116. {
  117. if (m_SystemInputModules[i] && m_SystemInputModules[i].IsActive())
  118. continue;
  119. m_SystemInputModules.RemoveAt(i);
  120. }
  121. }
  122. private bool m_SelectionGuard;
  123. /// <summary>
  124. /// Returns true if the EventSystem is already in a SetSelectedGameObject.
  125. /// </summary>
  126. public bool alreadySelecting
  127. {
  128. get { return m_SelectionGuard; }
  129. }
  130. /// <summary>
  131. /// Set the object as selected. Will send an OnDeselect the the old selected object and OnSelect to the new selected object.
  132. /// </summary>
  133. /// <param name="selected">GameObject to select.</param>
  134. /// <param name="pointer">Associated EventData.</param>
  135. public void SetSelectedGameObject(GameObject selected, BaseEventData pointer)
  136. {
  137. if (m_SelectionGuard)
  138. {
  139. Debug.LogError("Attempting to select " + selected + "while already selecting an object.");
  140. return;
  141. }
  142. m_SelectionGuard = true;
  143. if (selected == m_CurrentSelected)
  144. {
  145. m_SelectionGuard = false;
  146. return;
  147. }
  148. // Debug.Log("Selection: new (" + selected + ") old (" + m_CurrentSelected + ")");
  149. ExecuteEvents.Execute(m_CurrentSelected, pointer, ExecuteEvents.deselectHandler);
  150. m_CurrentSelected = selected;
  151. ExecuteEvents.Execute(m_CurrentSelected, pointer, ExecuteEvents.selectHandler);
  152. m_SelectionGuard = false;
  153. }
  154. private BaseEventData m_DummyData;
  155. private BaseEventData baseEventDataCache
  156. {
  157. get
  158. {
  159. if (m_DummyData == null)
  160. m_DummyData = new BaseEventData(this);
  161. return m_DummyData;
  162. }
  163. }
  164. /// <summary>
  165. /// Set the object as selected. Will send an OnDeselect the the old selected object and OnSelect to the new selected object.
  166. /// </summary>
  167. /// <param name="selected">GameObject to select.</param>
  168. public void SetSelectedGameObject(GameObject selected)
  169. {
  170. SetSelectedGameObject(selected, baseEventDataCache);
  171. }
  172. private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
  173. {
  174. if (lhs.module != rhs.module)
  175. {
  176. var lhsEventCamera = lhs.module.eventCamera;
  177. var rhsEventCamera = rhs.module.eventCamera;
  178. if (lhsEventCamera != null && rhsEventCamera != null && lhsEventCamera.depth != rhsEventCamera.depth)
  179. {
  180. // need to reverse the standard compareTo
  181. if (lhsEventCamera.depth < rhsEventCamera.depth)
  182. return 1;
  183. if (lhsEventCamera.depth == rhsEventCamera.depth)
  184. return 0;
  185. return -1;
  186. }
  187. if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
  188. return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);
  189. if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
  190. return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
  191. }
  192. // Renderer sorting
  193. if (lhs.sortingLayer != rhs.sortingLayer)
  194. {
  195. // Uses the layer value to properly compare the relative order of the layers.
  196. var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
  197. var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
  198. return rid.CompareTo(lid);
  199. }
  200. if (lhs.sortingOrder != rhs.sortingOrder)
  201. return rhs.sortingOrder.CompareTo(lhs.sortingOrder);
  202. // comparing depth only makes sense if the two raycast results have the same root canvas (case 912396)
  203. if (lhs.depth != rhs.depth && lhs.module.rootRaycaster == rhs.module.rootRaycaster)
  204. return rhs.depth.CompareTo(lhs.depth);
  205. if (lhs.distance != rhs.distance)
  206. return lhs.distance.CompareTo(rhs.distance);
  207. #if PACKAGE_PHYSICS2D
  208. // Sorting group
  209. if (lhs.sortingGroupID != SortingGroup.invalidSortingGroupID && rhs.sortingGroupID != SortingGroup.invalidSortingGroupID)
  210. {
  211. if (lhs.sortingGroupID != rhs.sortingGroupID)
  212. return lhs.sortingGroupID.CompareTo(rhs.sortingGroupID);
  213. if (lhs.sortingGroupOrder != rhs.sortingGroupOrder)
  214. return rhs.sortingGroupOrder.CompareTo(lhs.sortingGroupOrder);
  215. }
  216. #endif
  217. return lhs.index.CompareTo(rhs.index);
  218. }
  219. private static readonly Comparison<RaycastResult> s_RaycastComparer = RaycastComparer;
  220. /// <summary>
  221. /// Raycast into the scene using all configured BaseRaycasters.
  222. /// </summary>
  223. /// <param name="eventData">Current pointer data.</param>
  224. /// <param name="raycastResults">List of 'hits' to populate.</param>
  225. public void RaycastAll(PointerEventData eventData, List<RaycastResult> raycastResults)
  226. {
  227. raycastResults.Clear();
  228. var modules = RaycasterManager.GetRaycasters();
  229. var modulesCount = modules.Count;
  230. for (int i = 0; i < modulesCount; ++i)
  231. {
  232. var module = modules[i];
  233. if (module == null || !module.IsActive())
  234. continue;
  235. module.Raycast(eventData, raycastResults);
  236. }
  237. raycastResults.Sort(s_RaycastComparer);
  238. }
  239. /// <summary>
  240. /// Is the pointer with the given ID over an EventSystem object?
  241. /// </summary>
  242. public bool IsPointerOverGameObject()
  243. {
  244. return IsPointerOverGameObject(PointerInputModule.kMouseLeftId);
  245. }
  246. /// <summary>
  247. /// Is the pointer with the given ID over an EventSystem object?
  248. /// </summary>
  249. /// <remarks>
  250. /// If you use IsPointerOverGameObject() without a parameter, it points to the "left mouse button" (pointerId = -1); therefore when you use IsPointerOverGameObject for touch, you should consider passing a pointerId to it
  251. /// Note that for touch, IsPointerOverGameObject should be used with ''OnMouseDown()'' or ''Input.GetMouseButtonDown(0)'' or ''Input.GetTouch(0).phase == TouchPhase.Began''.
  252. /// </remarks>
  253. /// <example>
  254. /// <code>
  255. /// <![CDATA[
  256. /// using UnityEngine;
  257. /// using System.Collections;
  258. /// using UnityEngine.EventSystems;
  259. ///
  260. /// public class MouseExample : MonoBehaviour
  261. /// {
  262. /// void Update()
  263. /// {
  264. /// // Check if the left mouse button was clicked
  265. /// if (Input.GetMouseButtonDown(0))
  266. /// {
  267. /// // Check if the mouse was clicked over a UI element
  268. /// if (EventSystem.current.IsPointerOverGameObject())
  269. /// {
  270. /// Debug.Log("Clicked on the UI");
  271. /// }
  272. /// }
  273. /// }
  274. /// }
  275. /// ]]>
  276. ///</code>
  277. /// </example>
  278. public bool IsPointerOverGameObject(int pointerId)
  279. {
  280. return m_CurrentInputModule != null && m_CurrentInputModule.IsPointerOverGameObject(pointerId);
  281. }
  282. // This code is disabled unless the UI Toolkit package or the com.unity.modules.uielements module are present.
  283. // The UIElements module is always present in the Editor but it can be stripped from a project build if unused.
  284. #if PACKAGE_UITOOLKIT
  285. private struct UIToolkitOverrideConfig
  286. {
  287. public EventSystem activeEventSystem;
  288. public bool sendEvents;
  289. public bool createPanelGameObjectsOnStart;
  290. }
  291. private static UIToolkitOverrideConfig s_UIToolkitOverride = new UIToolkitOverrideConfig
  292. {
  293. activeEventSystem = null,
  294. sendEvents = true,
  295. createPanelGameObjectsOnStart = true
  296. };
  297. private bool isUIToolkitActiveEventSystem =>
  298. s_UIToolkitOverride.activeEventSystem == this || s_UIToolkitOverride.activeEventSystem == null;
  299. private bool sendUIToolkitEvents =>
  300. s_UIToolkitOverride.sendEvents && isUIToolkitActiveEventSystem;
  301. private bool createUIToolkitPanelGameObjectsOnStart =>
  302. s_UIToolkitOverride.createPanelGameObjectsOnStart && isUIToolkitActiveEventSystem;
  303. #endif
  304. /// <summary>
  305. /// Sets how UI Toolkit runtime panels receive events and handle selection
  306. /// when interacting with other objects that use the EventSystem, such as components from the Unity UI package.
  307. /// </summary>
  308. /// <param name="activeEventSystem">
  309. /// The EventSystem used to override UI Toolkit panel events and selection.
  310. /// If activeEventSystem is null, UI Toolkit panels will use current enabled EventSystem
  311. /// or, if there is none, the default InputManager-based event system will be used.
  312. /// </param>
  313. /// <param name="sendEvents">
  314. /// If true, UI Toolkit events will come from this EventSystem
  315. /// instead of the default InputManager-based event system.
  316. /// </param>
  317. /// <param name="createPanelGameObjectsOnStart">
  318. /// If true, UI Toolkit panels' unassigned selectableGameObject will be automatically initialized
  319. /// with children GameObjects of this EventSystem on Start.
  320. /// </param>
  321. public static void SetUITookitEventSystemOverride(EventSystem activeEventSystem, bool sendEvents = true, bool createPanelGameObjectsOnStart = true)
  322. {
  323. #if PACKAGE_UITOOLKIT
  324. UIElementsRuntimeUtility.UnregisterEventSystem(UIElementsRuntimeUtility.activeEventSystem);
  325. s_UIToolkitOverride = new UIToolkitOverrideConfig
  326. {
  327. activeEventSystem = activeEventSystem,
  328. sendEvents = sendEvents,
  329. createPanelGameObjectsOnStart = createPanelGameObjectsOnStart,
  330. };
  331. if (sendEvents)
  332. {
  333. var eventSystem = activeEventSystem != null ? activeEventSystem : EventSystem.current;
  334. if (eventSystem.isActiveAndEnabled)
  335. UIElementsRuntimeUtility.RegisterEventSystem(activeEventSystem);
  336. }
  337. #endif
  338. }
  339. #if PACKAGE_UITOOLKIT
  340. private bool m_Started;
  341. private bool m_IsTrackingUIToolkitPanels;
  342. private void StartTrackingUIToolkitPanels()
  343. {
  344. if (createUIToolkitPanelGameObjectsOnStart)
  345. {
  346. foreach (BaseRuntimePanel panel in UIElementsRuntimeUtility.GetSortedPlayerPanels())
  347. {
  348. CreateUIToolkitPanelGameObject(panel);
  349. }
  350. UIElementsRuntimeUtility.onCreatePanel += CreateUIToolkitPanelGameObject;
  351. m_IsTrackingUIToolkitPanels = true;
  352. }
  353. }
  354. private void StopTrackingUIToolkitPanels()
  355. {
  356. if (m_IsTrackingUIToolkitPanels)
  357. {
  358. UIElementsRuntimeUtility.onCreatePanel -= CreateUIToolkitPanelGameObject;
  359. m_IsTrackingUIToolkitPanels = false;
  360. }
  361. }
  362. private void CreateUIToolkitPanelGameObject(BaseRuntimePanel panel)
  363. {
  364. if (panel.selectableGameObject == null)
  365. {
  366. var go = new GameObject(panel.name, typeof(PanelEventHandler), typeof(PanelRaycaster));
  367. go.transform.SetParent(transform);
  368. panel.selectableGameObject = go;
  369. panel.destroyed += () => DestroyImmediate(go);
  370. }
  371. }
  372. #endif
  373. protected override void Start()
  374. {
  375. base.Start();
  376. #if PACKAGE_UITOOLKIT
  377. m_Started = true;
  378. StartTrackingUIToolkitPanels();
  379. #endif
  380. }
  381. protected override void OnEnable()
  382. {
  383. base.OnEnable();
  384. m_EventSystems.Add(this);
  385. #if PACKAGE_UITOOLKIT
  386. if (m_Started && !m_IsTrackingUIToolkitPanels)
  387. {
  388. StartTrackingUIToolkitPanels();
  389. }
  390. if (sendUIToolkitEvents)
  391. {
  392. UIElementsRuntimeUtility.RegisterEventSystem(this);
  393. }
  394. #endif
  395. }
  396. protected override void OnDisable()
  397. {
  398. #if PACKAGE_UITOOLKIT
  399. StopTrackingUIToolkitPanels();
  400. UIElementsRuntimeUtility.UnregisterEventSystem(this);
  401. #endif
  402. if (m_CurrentInputModule != null)
  403. {
  404. m_CurrentInputModule.DeactivateModule();
  405. m_CurrentInputModule = null;
  406. }
  407. m_EventSystems.Remove(this);
  408. base.OnDisable();
  409. }
  410. private void TickModules()
  411. {
  412. var systemInputModulesCount = m_SystemInputModules.Count;
  413. for (var i = 0; i < systemInputModulesCount; i++)
  414. {
  415. if (m_SystemInputModules[i] != null)
  416. m_SystemInputModules[i].UpdateModule();
  417. }
  418. }
  419. protected virtual void OnApplicationFocus(bool hasFocus)
  420. {
  421. m_HasFocus = hasFocus;
  422. if (!m_HasFocus)
  423. TickModules();
  424. }
  425. protected virtual void Update()
  426. {
  427. if (current != this)
  428. return;
  429. TickModules();
  430. bool changedModule = false;
  431. var systemInputModulesCount = m_SystemInputModules.Count;
  432. for (var i = 0; i < systemInputModulesCount; i++)
  433. {
  434. var module = m_SystemInputModules[i];
  435. if (module.IsModuleSupported() && module.ShouldActivateModule())
  436. {
  437. if (m_CurrentInputModule != module)
  438. {
  439. ChangeEventModule(module);
  440. changedModule = true;
  441. }
  442. break;
  443. }
  444. }
  445. // no event module set... set the first valid one...
  446. if (m_CurrentInputModule == null)
  447. {
  448. for (var i = 0; i < systemInputModulesCount; i++)
  449. {
  450. var module = m_SystemInputModules[i];
  451. if (module.IsModuleSupported())
  452. {
  453. ChangeEventModule(module);
  454. changedModule = true;
  455. break;
  456. }
  457. }
  458. }
  459. if (!changedModule && m_CurrentInputModule != null)
  460. m_CurrentInputModule.Process();
  461. #if UNITY_EDITOR
  462. if (Application.isPlaying)
  463. {
  464. int eventSystemCount = 0;
  465. for (int i = 0; i < m_EventSystems.Count; i++)
  466. {
  467. if (m_EventSystems[i].GetType() == typeof(EventSystem))
  468. eventSystemCount++;
  469. }
  470. if (eventSystemCount > 1)
  471. Debug.LogWarning("There are " + eventSystemCount + " event systems in the scene. Please ensure there is always exactly one event system in the scene");
  472. }
  473. #endif
  474. }
  475. private void ChangeEventModule(BaseInputModule module)
  476. {
  477. if (m_CurrentInputModule == module)
  478. return;
  479. if (m_CurrentInputModule != null)
  480. m_CurrentInputModule.DeactivateModule();
  481. if (module != null)
  482. module.ActivateModule();
  483. m_CurrentInputModule = module;
  484. }
  485. public override string ToString()
  486. {
  487. var sb = new StringBuilder();
  488. sb.AppendLine("<b>Selected:</b>" + currentSelectedGameObject);
  489. sb.AppendLine();
  490. sb.AppendLine();
  491. sb.AppendLine(m_CurrentInputModule != null ? m_CurrentInputModule.ToString() : "No module");
  492. return sb.ToString();
  493. }
  494. }
  495. }