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