A game about forced loneliness, made by TACStudios
at master 663 lines 25 kB view raw
1using System; 2using UnityEngine; 3using UnityEngine.Serialization; 4 5namespace UnityEngine.EventSystems 6{ 7 [AddComponentMenu("Event/Standalone Input Module")] 8 /// <summary> 9 /// A BaseInputModule designed for mouse / keyboard / controller input. 10 /// </summary> 11 /// <remarks> 12 /// Input module for working with, mouse, keyboard, or controller. 13 /// </remarks> 14 public class StandaloneInputModule : PointerInputModule 15 { 16 private float m_PrevActionTime; 17 private Vector2 m_LastMoveVector; 18 private int m_ConsecutiveMoveCount = 0; 19 20 private Vector2 m_LastMousePosition; 21 private Vector2 m_MousePosition; 22 23 private GameObject m_CurrentFocusedGameObject; 24 25 private PointerEventData m_InputPointerEvent; 26 27 private const float doubleClickTime = 0.3f; 28 29 protected StandaloneInputModule() 30 { 31 } 32 33 [Obsolete("Mode is no longer needed on input module as it handles both mouse and keyboard simultaneously.", false)] 34 public enum InputMode 35 { 36 Mouse, 37 Buttons 38 } 39 40 [Obsolete("Mode is no longer needed on input module as it handles both mouse and keyboard simultaneously.", false)] 41 public InputMode inputMode 42 { 43 get { return InputMode.Mouse; } 44 } 45 46 [SerializeField] 47 private string m_HorizontalAxis = "Horizontal"; 48 49 /// <summary> 50 /// Name of the vertical axis for movement (if axis events are used). 51 /// </summary> 52 [SerializeField] 53 private string m_VerticalAxis = "Vertical"; 54 55 /// <summary> 56 /// Name of the submit button. 57 /// </summary> 58 [SerializeField] 59 private string m_SubmitButton = "Submit"; 60 61 /// <summary> 62 /// Name of the submit button. 63 /// </summary> 64 [SerializeField] 65 private string m_CancelButton = "Cancel"; 66 67 [SerializeField] 68 private float m_InputActionsPerSecond = 10; 69 70 [SerializeField] 71 private float m_RepeatDelay = 0.5f; 72 73 [SerializeField] 74 [FormerlySerializedAs("m_AllowActivationOnMobileDevice")] 75 [HideInInspector] 76 private bool m_ForceModuleActive; 77 78 [Obsolete("allowActivationOnMobileDevice has been deprecated. Use forceModuleActive instead (UnityUpgradable) -> forceModuleActive")] 79 public bool allowActivationOnMobileDevice 80 { 81 get { return m_ForceModuleActive; } 82 set { m_ForceModuleActive = value; } 83 } 84 85 /// <summary> 86 /// Force this module to be active. 87 /// </summary> 88 /// <remarks> 89 /// If there is no module active with higher priority (ordered in the inspector) this module will be forced active even if valid enabling conditions are not met. 90 /// </remarks> 91 92 [Obsolete("forceModuleActive has been deprecated. There is no need to force the module awake as StandaloneInputModule works for all platforms")] 93 public bool forceModuleActive 94 { 95 get { return m_ForceModuleActive; } 96 set { m_ForceModuleActive = value; } 97 } 98 99 /// <summary> 100 /// Number of keyboard / controller inputs allowed per second. 101 /// </summary> 102 public float inputActionsPerSecond 103 { 104 get { return m_InputActionsPerSecond; } 105 set { m_InputActionsPerSecond = value; } 106 } 107 108 /// <summary> 109 /// Delay in seconds before the input actions per second repeat rate takes effect. 110 /// </summary> 111 /// <remarks> 112 /// If the same direction is sustained, the inputActionsPerSecond property can be used to control the rate at which events are fired. However, it can be desirable that the first repetition is delayed, so the user doesn't get repeated actions by accident. 113 /// </remarks> 114 public float repeatDelay 115 { 116 get { return m_RepeatDelay; } 117 set { m_RepeatDelay = value; } 118 } 119 120 /// <summary> 121 /// Name of the horizontal axis for movement (if axis events are used). 122 /// </summary> 123 public string horizontalAxis 124 { 125 get { return m_HorizontalAxis; } 126 set { m_HorizontalAxis = value; } 127 } 128 129 /// <summary> 130 /// Name of the vertical axis for movement (if axis events are used). 131 /// </summary> 132 public string verticalAxis 133 { 134 get { return m_VerticalAxis; } 135 set { m_VerticalAxis = value; } 136 } 137 138 /// <summary> 139 /// Maximum number of input events handled per second. 140 /// </summary> 141 public string submitButton 142 { 143 get { return m_SubmitButton; } 144 set { m_SubmitButton = value; } 145 } 146 147 /// <summary> 148 /// Input manager name for the 'cancel' button. 149 /// </summary> 150 public string cancelButton 151 { 152 get { return m_CancelButton; } 153 set { m_CancelButton = value; } 154 } 155 156 private bool ShouldIgnoreEventsOnNoFocus() 157 { 158#if UNITY_EDITOR 159 return !UnityEditor.EditorApplication.isRemoteConnected; 160#else 161 return true; 162#endif 163 } 164 165 public override void UpdateModule() 166 { 167 if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus()) 168 { 169 if (m_InputPointerEvent != null && m_InputPointerEvent.pointerDrag != null && m_InputPointerEvent.dragging) 170 { 171 ReleaseMouse(m_InputPointerEvent, m_InputPointerEvent.pointerCurrentRaycast.gameObject); 172 } 173 174 m_InputPointerEvent = null; 175 176 return; 177 } 178 179 m_LastMousePosition = m_MousePosition; 180 m_MousePosition = input.mousePosition; 181 } 182 183 private void ReleaseMouse(PointerEventData pointerEvent, GameObject currentOverGo) 184 { 185 ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler); 186 187 var pointerClickHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo); 188 189 // PointerClick and Drop events 190 if (pointerEvent.pointerClick == pointerClickHandler && pointerEvent.eligibleForClick) 191 { 192 ExecuteEvents.Execute(pointerEvent.pointerClick, pointerEvent, ExecuteEvents.pointerClickHandler); 193 } 194 if (pointerEvent.pointerDrag != null && pointerEvent.dragging) 195 { 196 ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler); 197 } 198 199 pointerEvent.eligibleForClick = false; 200 pointerEvent.pointerPress = null; 201 pointerEvent.rawPointerPress = null; 202 pointerEvent.pointerClick = null; 203 204 if (pointerEvent.pointerDrag != null && pointerEvent.dragging) 205 ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler); 206 207 pointerEvent.dragging = false; 208 pointerEvent.pointerDrag = null; 209 210 // redo pointer enter / exit to refresh state 211 // so that if we moused over something that ignored it before 212 // due to having pressed on something else 213 // it now gets it. 214 if (currentOverGo != pointerEvent.pointerEnter) 215 { 216 HandlePointerExitAndEnter(pointerEvent, null); 217 HandlePointerExitAndEnter(pointerEvent, currentOverGo); 218 } 219 220 m_InputPointerEvent = pointerEvent; 221 } 222 223 public override bool ShouldActivateModule() 224 { 225 if (!base.ShouldActivateModule()) 226 return false; 227 228 var shouldActivate = m_ForceModuleActive; 229 shouldActivate |= input.GetButtonDown(m_SubmitButton); 230 shouldActivate |= input.GetButtonDown(m_CancelButton); 231 shouldActivate |= !Mathf.Approximately(input.GetAxisRaw(m_HorizontalAxis), 0.0f); 232 shouldActivate |= !Mathf.Approximately(input.GetAxisRaw(m_VerticalAxis), 0.0f); 233 shouldActivate |= (m_MousePosition - m_LastMousePosition).sqrMagnitude > 0.0f; 234 shouldActivate |= input.GetMouseButtonDown(0); 235 236 if (input.touchCount > 0) 237 shouldActivate = true; 238 239 return shouldActivate; 240 } 241 242 /// <summary> 243 /// See BaseInputModule. 244 /// </summary> 245 public override void ActivateModule() 246 { 247 if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus()) 248 return; 249 250 base.ActivateModule(); 251 m_MousePosition = input.mousePosition; 252 m_LastMousePosition = input.mousePosition; 253 254 var toSelect = eventSystem.currentSelectedGameObject; 255 if (toSelect == null) 256 toSelect = eventSystem.firstSelectedGameObject; 257 258 eventSystem.SetSelectedGameObject(toSelect, GetBaseEventData()); 259 } 260 261 /// <summary> 262 /// See BaseInputModule. 263 /// </summary> 264 public override void DeactivateModule() 265 { 266 base.DeactivateModule(); 267 ClearSelection(); 268 } 269 270 public override void Process() 271 { 272 if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus()) 273 return; 274 275 bool usedEvent = SendUpdateEventToSelectedObject(); 276 277 // case 1004066 - touch / mouse events should be processed before navigation events in case 278 // they change the current selected gameobject and the submit button is a touch / mouse button. 279 280 // touch needs to take precedence because of the mouse emulation layer 281 if (!ProcessTouchEvents() && input.mousePresent) 282 ProcessMouseEvent(); 283 284 if (eventSystem.sendNavigationEvents) 285 { 286 if (!usedEvent) 287 usedEvent |= SendMoveEventToSelectedObject(); 288 289 if (!usedEvent) 290 SendSubmitEventToSelectedObject(); 291 } 292 } 293 294 private bool ProcessTouchEvents() 295 { 296 for (int i = 0; i < input.touchCount; ++i) 297 { 298 Touch touch = input.GetTouch(i); 299 300 if (touch.type == TouchType.Indirect) 301 continue; 302 303 bool released; 304 bool pressed; 305 var pointer = GetTouchPointerEventData(touch, out pressed, out released); 306 307 ProcessTouchPress(pointer, pressed, released); 308 309 if (!released) 310 { 311 ProcessMove(pointer); 312 ProcessDrag(pointer); 313 } 314 else 315 RemovePointerData(pointer); 316 } 317 return input.touchCount > 0; 318 } 319 320 /// <summary> 321 /// This method is called by Unity whenever a touch event is processed. Override this method with a custom implementation to process touch events yourself. 322 /// </summary> 323 /// <param name="pointerEvent">Event data relating to the touch event, such as position and ID to be passed to the touch event destination object.</param> 324 /// <param name="pressed">This is true for the first frame of a touch event, and false thereafter. This can therefore be used to determine the instant a touch event occurred.</param> 325 /// <param name="released">This is true only for the last frame of a touch event.</param> 326 /// <remarks> 327 /// This method can be overridden in derived classes to change how touch press events are handled. 328 /// </remarks> 329 protected void ProcessTouchPress(PointerEventData pointerEvent, bool pressed, bool released) 330 { 331 var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject; 332 333 // PointerDown notification 334 if (pressed) 335 { 336 pointerEvent.eligibleForClick = true; 337 pointerEvent.delta = Vector2.zero; 338 pointerEvent.dragging = false; 339 pointerEvent.useDragThreshold = true; 340 pointerEvent.pressPosition = pointerEvent.position; 341 pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast; 342 343 DeselectIfSelectionChanged(currentOverGo, pointerEvent); 344 345 if (pointerEvent.pointerEnter != currentOverGo) 346 { 347 // send a pointer enter to the touched element if it isn't the one to select... 348 HandlePointerExitAndEnter(pointerEvent, currentOverGo); 349 pointerEvent.pointerEnter = currentOverGo; 350 } 351 352 var resetDiffTime = Time.unscaledTime - pointerEvent.clickTime; 353 if (resetDiffTime >= doubleClickTime) 354 { 355 pointerEvent.clickCount = 0; 356 } 357 358 // search for the control that will receive the press 359 // if we can't find a press handler set the press 360 // handler to be what would receive a click. 361 var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler); 362 363 var newClick = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo); 364 365 // didnt find a press handler... search for a click handler 366 if (newPressed == null) 367 newPressed = newClick; 368 369 // Debug.Log("Pressed: " + newPressed); 370 371 float time = Time.unscaledTime; 372 373 if (newPressed == pointerEvent.lastPress) 374 { 375 var diffTime = time - pointerEvent.clickTime; 376 if (diffTime < doubleClickTime) 377 ++pointerEvent.clickCount; 378 else 379 pointerEvent.clickCount = 1; 380 381 pointerEvent.clickTime = time; 382 } 383 else 384 { 385 pointerEvent.clickCount = 1; 386 } 387 388 pointerEvent.pointerPress = newPressed; 389 pointerEvent.rawPointerPress = currentOverGo; 390 pointerEvent.pointerClick = newClick; 391 392 pointerEvent.clickTime = time; 393 394 // Save the drag handler as well 395 pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo); 396 397 if (pointerEvent.pointerDrag != null) 398 ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag); 399 } 400 401 // PointerUp notification 402 if (released) 403 { 404 // Debug.Log("Executing pressup on: " + pointer.pointerPress); 405 ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler); 406 407 // Debug.Log("KeyCode: " + pointer.eventData.keyCode); 408 409 // see if we mouse up on the same element that we clicked on... 410 var pointerClickHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo); 411 412 // PointerClick and Drop events 413 if (pointerEvent.pointerClick == pointerClickHandler && pointerEvent.eligibleForClick) 414 { 415 ExecuteEvents.Execute(pointerEvent.pointerClick, pointerEvent, ExecuteEvents.pointerClickHandler); 416 } 417 418 if (pointerEvent.pointerDrag != null && pointerEvent.dragging) 419 { 420 ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler); 421 } 422 423 pointerEvent.eligibleForClick = false; 424 pointerEvent.pointerPress = null; 425 pointerEvent.rawPointerPress = null; 426 pointerEvent.pointerClick = null; 427 428 if (pointerEvent.pointerDrag != null && pointerEvent.dragging) 429 ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler); 430 431 pointerEvent.dragging = false; 432 pointerEvent.pointerDrag = null; 433 434 // send exit events as we need to simulate this on touch up on touch device 435 ExecuteEvents.ExecuteHierarchy(pointerEvent.pointerEnter, pointerEvent, ExecuteEvents.pointerExitHandler); 436 pointerEvent.pointerEnter = null; 437 } 438 439 m_InputPointerEvent = pointerEvent; 440 } 441 442 /// <summary> 443 /// Calculate and send a submit event to the current selected object. 444 /// </summary> 445 /// <returns>If the submit event was used by the selected object.</returns> 446 protected bool SendSubmitEventToSelectedObject() 447 { 448 if (eventSystem.currentSelectedGameObject == null) 449 return false; 450 451 var data = GetBaseEventData(); 452 if (input.GetButtonDown(m_SubmitButton)) 453 ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.submitHandler); 454 455 if (input.GetButtonDown(m_CancelButton)) 456 ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.cancelHandler); 457 return data.used; 458 } 459 460 private Vector2 GetRawMoveVector() 461 { 462 Vector2 move = Vector2.zero; 463 move.x = input.GetAxisRaw(m_HorizontalAxis); 464 move.y = input.GetAxisRaw(m_VerticalAxis); 465 466 if (input.GetButtonDown(m_HorizontalAxis)) 467 { 468 if (move.x < 0) 469 move.x = -1f; 470 if (move.x > 0) 471 move.x = 1f; 472 } 473 if (input.GetButtonDown(m_VerticalAxis)) 474 { 475 if (move.y < 0) 476 move.y = -1f; 477 if (move.y > 0) 478 move.y = 1f; 479 } 480 return move; 481 } 482 483 /// <summary> 484 /// Calculate and send a move event to the current selected object. 485 /// </summary> 486 /// <returns>If the move event was used by the selected object.</returns> 487 protected bool SendMoveEventToSelectedObject() 488 { 489 float time = Time.unscaledTime; 490 491 Vector2 movement = GetRawMoveVector(); 492 if (Mathf.Approximately(movement.x, 0f) && Mathf.Approximately(movement.y, 0f)) 493 { 494 m_ConsecutiveMoveCount = 0; 495 return false; 496 } 497 498 bool similarDir = (Vector2.Dot(movement, m_LastMoveVector) > 0); 499 500 // If direction didn't change at least 90 degrees, wait for delay before allowing consequtive event. 501 if (similarDir && m_ConsecutiveMoveCount == 1) 502 { 503 if (time <= m_PrevActionTime + m_RepeatDelay) 504 return false; 505 } 506 // If direction changed at least 90 degree, or we already had the delay, repeat at repeat rate. 507 else 508 { 509 if (time <= m_PrevActionTime + 1f / m_InputActionsPerSecond) 510 return false; 511 } 512 513 var axisEventData = GetAxisEventData(movement.x, movement.y, 0.6f); 514 515 if (axisEventData.moveDir != MoveDirection.None) 516 { 517 ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, axisEventData, ExecuteEvents.moveHandler); 518 if (!similarDir) 519 m_ConsecutiveMoveCount = 0; 520 m_ConsecutiveMoveCount++; 521 m_PrevActionTime = time; 522 m_LastMoveVector = movement; 523 } 524 else 525 { 526 m_ConsecutiveMoveCount = 0; 527 } 528 529 return axisEventData.used; 530 } 531 532 protected void ProcessMouseEvent() 533 { 534 ProcessMouseEvent(0); 535 } 536 537 [Obsolete("This method is no longer checked, overriding it with return true does nothing!")] 538 protected virtual bool ForceAutoSelect() 539 { 540 return false; 541 } 542 543 /// <summary> 544 /// Process all mouse events. 545 /// </summary> 546 protected void ProcessMouseEvent(int id) 547 { 548 var mouseData = GetMousePointerEventData(id); 549 var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData; 550 551 m_CurrentFocusedGameObject = leftButtonData.buttonData.pointerCurrentRaycast.gameObject; 552 553 // Process the first mouse button fully 554 ProcessMousePress(leftButtonData); 555 ProcessMove(leftButtonData.buttonData); 556 ProcessDrag(leftButtonData.buttonData); 557 558 // Now process right / middle clicks 559 ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData); 560 ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData.buttonData); 561 ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData); 562 ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData.buttonData); 563 564 if (!Mathf.Approximately(leftButtonData.buttonData.scrollDelta.sqrMagnitude, 0.0f)) 565 { 566 var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler>(leftButtonData.buttonData.pointerCurrentRaycast.gameObject); 567 ExecuteEvents.ExecuteHierarchy(scrollHandler, leftButtonData.buttonData, ExecuteEvents.scrollHandler); 568 } 569 } 570 571 protected bool SendUpdateEventToSelectedObject() 572 { 573 if (eventSystem.currentSelectedGameObject == null) 574 return false; 575 576 var data = GetBaseEventData(); 577 ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler); 578 return data.used; 579 } 580 581 /// <summary> 582 /// Calculate and process any mouse button state changes. 583 /// </summary> 584 protected void ProcessMousePress(MouseButtonEventData data) 585 { 586 var pointerEvent = data.buttonData; 587 var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject; 588 589 // PointerDown notification 590 if (data.PressedThisFrame()) 591 { 592 pointerEvent.eligibleForClick = true; 593 pointerEvent.delta = Vector2.zero; 594 pointerEvent.dragging = false; 595 pointerEvent.useDragThreshold = true; 596 pointerEvent.pressPosition = pointerEvent.position; 597 pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast; 598 599 DeselectIfSelectionChanged(currentOverGo, pointerEvent); 600 601 var resetDiffTime = Time.unscaledTime - pointerEvent.clickTime; 602 if (resetDiffTime >= doubleClickTime) 603 { 604 pointerEvent.clickCount = 0; 605 } 606 607 // search for the control that will receive the press 608 // if we can't find a press handler set the press 609 // handler to be what would receive a click. 610 var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler); 611 var newClick = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo); 612 613 // didnt find a press handler... search for a click handler 614 if (newPressed == null) 615 newPressed = newClick; 616 617 // Debug.Log("Pressed: " + newPressed); 618 619 float time = Time.unscaledTime; 620 621 if (newPressed == pointerEvent.lastPress) 622 { 623 var diffTime = time - pointerEvent.clickTime; 624 if (diffTime < doubleClickTime) 625 ++pointerEvent.clickCount; 626 else 627 pointerEvent.clickCount = 1; 628 629 pointerEvent.clickTime = time; 630 } 631 else 632 { 633 pointerEvent.clickCount = 1; 634 } 635 636 pointerEvent.pointerPress = newPressed; 637 pointerEvent.rawPointerPress = currentOverGo; 638 pointerEvent.pointerClick = newClick; 639 640 pointerEvent.clickTime = time; 641 642 // Save the drag handler as well 643 pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo); 644 645 if (pointerEvent.pointerDrag != null) 646 ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag); 647 648 m_InputPointerEvent = pointerEvent; 649 } 650 651 // PointerUp notification 652 if (data.ReleasedThisFrame()) 653 { 654 ReleaseMouse(pointerEvent, currentOverGo); 655 } 656 } 657 658 protected GameObject GetCurrentFocusedGameObject() 659 { 660 return m_CurrentFocusedGameObject; 661 } 662 } 663}