/* * StandaloneInputModule : * PointerInputModule : * BaseInputModule */ using System; using UnityEngine; using UnityEngine.Serialization; namespace UnityEngine.EventSystems { [AddComponentMenu("Event/Standalone Input Module")] public class StandaloneInputModule : PointerInputModule { private float m_PrevActionTime; private Vector2 m_LastMoveVector; private int m_ConsecutiveMoveCount = 0; // 上一个鼠标位置和当前鼠标位置,每帧更新 private Vector2 m_LastMousePosition; private Vector2 m_MousePosition; private GameObject m_CurrentFocusedGameObject; protected StandaloneInputModule() { } [Obsolete("Mode is no longer needed on input module as it handles both mouse and keyboard simultaneously.", false)] public enum InputMode { Mouse, Buttons } [Obsolete("Mode is no longer needed on input module as it handles both mouse and keyboard simultaneously.", false)] public InputMode inputMode { get { return InputMode.Mouse; } } [SerializeField] private string m_HorizontalAxis = "Horizontal"; /// /// Name of the vertical axis for movement (if axis events are used). /// [SerializeField] private string m_VerticalAxis = "Vertical"; /// /// Name of the submit button. /// [SerializeField] private string m_SubmitButton = "Submit"; /// /// Name of the submit button. /// [SerializeField] private string m_CancelButton = "Cancel"; [SerializeField] private float m_InputActionsPerSecond = 10; [SerializeField] private float m_RepeatDelay = 0.5f; [SerializeField] [FormerlySerializedAs("m_AllowActivationOnMobileDevice")] private bool m_ForceModuleActive; [Obsolete("allowActivationOnMobileDevice has been deprecated. Use forceModuleActive instead (UnityUpgradable) -> forceModuleActive")] public bool allowActivationOnMobileDevice { get { return m_ForceModuleActive; } set { m_ForceModuleActive = value; } } public bool forceModuleActive { get { return m_ForceModuleActive; } set { m_ForceModuleActive = value; } } public float inputActionsPerSecond { get { return m_InputActionsPerSecond; } set { m_InputActionsPerSecond = value; } } public float repeatDelay { get { return m_RepeatDelay; } set { m_RepeatDelay = value; } } /// /// Name of the horizontal axis for movement (if axis events are used). /// public string horizontalAxis { get { return m_HorizontalAxis; } set { m_HorizontalAxis = value; } } /// /// Name of the vertical axis for movement (if axis events are used). /// public string verticalAxis { get { return m_VerticalAxis; } set { m_VerticalAxis = value; } } public string submitButton { get { return m_SubmitButton; } set { m_SubmitButton = value; } } public string cancelButton { get { return m_CancelButton; } set { m_CancelButton = value; } } private bool ShouldIgnoreEventsOnNoFocus() { switch (SystemInfo.operatingSystemFamily) { case OperatingSystemFamily.Windows: case OperatingSystemFamily.Linux: case OperatingSystemFamily.MacOSX: #if UNITY_EDITOR if (UnityEditor.EditorApplication.isRemoteConnected) return false; #endif return true; default: return false; } } //c! 这里只更新鼠标位置 public override void UpdateModule() { // 如果没有获取焦点,且忽略没有焦点时的事件,返回(除非开启远程连接,否则会这样) if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus()) return; // 更新鼠标位置 m_LastMousePosition = m_MousePosition; m_MousePosition = input.mousePosition; } public override bool IsModuleSupported() { return m_ForceModuleActive || input.mousePresent || input.touchSupported; } public override bool ShouldActivateModule() { if (!base.ShouldActivateModule()) return false; var shouldActivate = m_ForceModuleActive; shouldActivate |= input.GetButtonDown(m_SubmitButton); shouldActivate |= input.GetButtonDown(m_CancelButton); shouldActivate |= !Mathf.Approximately(input.GetAxisRaw(m_HorizontalAxis), 0.0f); shouldActivate |= !Mathf.Approximately(input.GetAxisRaw(m_VerticalAxis), 0.0f); shouldActivate |= (m_MousePosition - m_LastMousePosition).sqrMagnitude > 0.0f; shouldActivate |= input.GetMouseButtonDown(0); if (input.touchCount > 0) shouldActivate = true; return shouldActivate; } public override void ActivateModule() { if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus()) return; base.ActivateModule(); m_MousePosition = input.mousePosition; m_LastMousePosition = input.mousePosition; var toSelect = eventSystem.currentSelectedGameObject; if (toSelect == null) toSelect = eventSystem.firstSelectedGameObject; eventSystem.SetSelectedGameObject(toSelect, GetBaseEventData()); } public override void DeactivateModule() { base.DeactivateModule(); ClearSelection(); } //c! input module事件处理主入口,每帧更新 public override void Process() { // 如果没有获取焦点,不需要处理 if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus()) return; //event 给选中的对象发布更新事件 bool usedEvent = SendUpdateEventToSelectedObject(); // 如果勾上了Send Navigation Events,发送move , submit, cancel事件 if (eventSystem.sendNavigationEvents) { if (!usedEvent) usedEvent |= SendMoveEventToSelectedObject(); //event 键盘方向键移动时给eventSystem选中的对象发布IMoveHandler事件 if (!usedEvent) SendSubmitEventToSelectedObject(); //event 如果按了相应按键,发布submit或者cancel事件 } // 模拟触摸或鼠标输入 // touch needs to take precedence because of the mouse emulation layer usedEvent = ProcessTouchEvents(); //event 检测屏幕触摸,PC上没有 if(!usedEvent && input.mousePresent) ProcessMouseEvent(); // 处理鼠标事件 } //c 触摸事件 private bool ProcessTouchEvents() { for (int i = 0; i < input.touchCount; ++i) //多点触控 { Debug.Log("Touch"); Touch touch = input.GetTouch(i); if (touch.type == TouchType.Indirect) continue; bool released; // 这是一个手指抬起操作 bool pressed; // 这是一个手指放下操作 // PointerEventData pointer = GetTouchPointerEventData(touch, out pressed, out released); // 射线检测并保存检测结果 // 处理触摸或抬起反馈,已经准备好了被触摸的物体 ProcessTouchPress(pointer, pressed, released); if (!released) { ProcessMove(pointer); ProcessDrag(pointer); } else RemovePointerData(pointer); } return input.touchCount > 0; } //c 处理触摸\抬起 protected void ProcessTouchPress(PointerEventData pointerEvent, bool pressed, bool released) { var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject; // 触摸反馈 // PointerDown notification if (pressed) { pointerEvent.eligibleForClick = true; pointerEvent.delta = Vector2.zero; pointerEvent.dragging = false; pointerEvent.useDragThreshold = true; pointerEvent.pressPosition = pointerEvent.position; pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast; DeselectIfSelectionChanged(currentOverGo, pointerEvent); if (pointerEvent.pointerEnter != currentOverGo) { // send a pointer enter to the touched element if it isn't the one to select... HandlePointerExitAndEnter(pointerEvent, currentOverGo); pointerEvent.pointerEnter = currentOverGo; } //event IPointerDownHandler // search for the control that will receive the press // if we can't find a press handler set the press // handler to be what would receive a click. var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler); // didnt find a press handler... search for a click handler if (newPressed == null) newPressed = ExecuteEvents.GetEventHandler(currentOverGo); // Debug.Log("Pressed: " + newPressed); float time = Time.unscaledTime; if (newPressed == pointerEvent.lastPress) { var diffTime = time - pointerEvent.clickTime; if (diffTime < 0.3f) ++pointerEvent.clickCount; else pointerEvent.clickCount = 1; pointerEvent.clickTime = time; } else { pointerEvent.clickCount = 1; } pointerEvent.pointerPress = newPressed; pointerEvent.rawPointerPress = currentOverGo; pointerEvent.clickTime = time; // Save the drag handler as well pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler(currentOverGo); if (pointerEvent.pointerDrag != null) ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag); } // 抬起反馈 // PointerUp notification if (released) { // Debug.Log("Executing pressup on: " + pointer.pointerPress); ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler); // Debug.Log("KeyCode: " + pointer.eventData.keyCode); // see if we mouse up on the same element that we clicked on... var pointerUpHandler = ExecuteEvents.GetEventHandler(currentOverGo); // 从这个对象开始往上找,直到一个挂了继承了IPointerClickHandler的组件 // PointerClick and Drop events if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick) //只有在pointerPress == pointerUpHandler时才会触发click事件 { ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler); } else if (pointerEvent.pointerDrag != null && pointerEvent.dragging) // 如果两者不相等,触发drop事件 { ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler); } pointerEvent.eligibleForClick = false; pointerEvent.pointerPress = null; pointerEvent.rawPointerPress = null; if (pointerEvent.pointerDrag != null && pointerEvent.dragging) ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler); pointerEvent.dragging = false; pointerEvent.pointerDrag = null; // send exit events as we need to simulate this on touch up on touch device ExecuteEvents.ExecuteHierarchy(pointerEvent.pointerEnter, pointerEvent, ExecuteEvents.pointerExitHandler); pointerEvent.pointerEnter = null; } } /// /// Process submit keys. /// protected bool SendSubmitEventToSelectedObject() { if (eventSystem.currentSelectedGameObject == null) return false; // 发布submit事件或者cancel事件 var data = GetBaseEventData(); if (input.GetButtonDown(m_SubmitButton)) // 这里是Project>Input里面配置的 ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.submitHandler); if (input.GetButtonDown(m_CancelButton)) ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.cancelHandler); return data.used; } private Vector2 GetRawMoveVector() { Vector2 move = Vector2.zero; move.x = input.GetAxisRaw(m_HorizontalAxis); move.y = input.GetAxisRaw(m_VerticalAxis); if (input.GetButtonDown(m_HorizontalAxis)) { if (move.x < 0) move.x = -1f; if (move.x > 0) move.x = 1f; } if (input.GetButtonDown(m_VerticalAxis)) { if (move.y < 0) move.y = -1f; if (move.y > 0) move.y = 1f; } return move; } /// /// Process keyboard events. /// protected bool SendMoveEventToSelectedObject() { float time = Time.unscaledTime; Vector2 movement = GetRawMoveVector(); if (Mathf.Approximately(movement.x, 0f) && Mathf.Approximately(movement.y, 0f)) // 如果键盘方向键没动,返回 { m_ConsecutiveMoveCount = 0; return false; } //allow即是否允许发布事件 // If user pressed key again, always allow event bool allow = input.GetButtonDown(m_HorizontalAxis) || input.GetButtonDown(m_VerticalAxis); bool similarDir = (Vector2.Dot(movement, m_LastMoveVector) > 0); // 和之前的方向是否夹角在90°以内,如果是的话说明很接近 if (!allow) { // Otherwise, user held down key or axis. // If direction didn't change at least 90 degrees, wait for delay before allowing consequtive event. if (similarDir && m_ConsecutiveMoveCount == 1) allow = (time > m_PrevActionTime + m_RepeatDelay); // If direction changed at least 90 degree, or we already had the delay, repeat at repeat rate. else allow = (time > m_PrevActionTime + 1f / m_InputActionsPerSecond); } if (!allow) return false; // 发布IMoveHandler事件 // Debug.Log(m_ProcessingEvent.rawType + " axis:" + m_AllowAxisEvents + " value:" + "(" + x + "," + y + ")"); var axisEventData = GetAxisEventData(movement.x, movement.y, 0.6f); if (axisEventData.moveDir != MoveDirection.None) { ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, axisEventData, ExecuteEvents.moveHandler); // IMoveHandler.OnMove if (!similarDir) m_ConsecutiveMoveCount = 0; m_ConsecutiveMoveCount++; m_PrevActionTime = time; m_LastMoveVector = movement; } else { m_ConsecutiveMoveCount = 0; } return axisEventData.used; } protected void ProcessMouseEvent() { ProcessMouseEvent(0); } [Obsolete("This method is no longer checked, overriding it with return true does nothing!")] protected virtual bool ForceAutoSelect() { return false; } /// /// Process all mouse events. /// protected void ProcessMouseEvent(int id) { var mouseData = GetMousePointerEventData(id); var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData; m_CurrentFocusedGameObject = leftButtonData.buttonData.pointerCurrentRaycast.gameObject; // Process the first mouse button fully ProcessMousePress(leftButtonData); ProcessMove(leftButtonData.buttonData); ProcessDrag(leftButtonData.buttonData); // Now process right / middle clicks ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData); ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData.buttonData); ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData); ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData.buttonData); if (!Mathf.Approximately(leftButtonData.buttonData.scrollDelta.sqrMagnitude, 0.0f)) { var scrollHandler = ExecuteEvents.GetEventHandler(leftButtonData.buttonData.pointerCurrentRaycast.gameObject); ExecuteEvents.ExecuteHierarchy(scrollHandler, leftButtonData.buttonData, ExecuteEvents.scrollHandler); } } // 每帧发送一个OnUpdateSelected事件 protected bool SendUpdateEventToSelectedObject() { if (eventSystem.currentSelectedGameObject == null) return false; var data = GetBaseEventData(); //event 向eventSystem当前选中的gameobject发布一个更新事件IUpdateSelectedHandler,比如inputfield ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler); return data.used; } // 处理鼠标按下事件,发送消息,调用回调 /// /// Process the current mouse press. /// protected void ProcessMousePress(MouseButtonEventData data) { var pointerEvent = data.buttonData; var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject; // PointerDown notification if (data.PressedThisFrame()) { pointerEvent.eligibleForClick = true; pointerEvent.delta = Vector2.zero; pointerEvent.dragging = false; pointerEvent.useDragThreshold = true; pointerEvent.pressPosition = pointerEvent.position; pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast; DeselectIfSelectionChanged(currentOverGo, pointerEvent); // search for the control that will receive the press // if we can't find a press handler set the press // handler to be what would receive a click. var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler); // didnt find a press handler... search for a click handler if (newPressed == null) newPressed = ExecuteEvents.GetEventHandler(currentOverGo); // Debug.Log("Pressed: " + newPressed); float time = Time.unscaledTime; if (newPressed == pointerEvent.lastPress) { var diffTime = time - pointerEvent.clickTime; if (diffTime < 0.3f) ++pointerEvent.clickCount; else pointerEvent.clickCount = 1; pointerEvent.clickTime = time; } else { pointerEvent.clickCount = 1; } pointerEvent.pointerPress = newPressed; pointerEvent.rawPointerPress = currentOverGo; pointerEvent.clickTime = time; // Save the drag handler as well pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler(currentOverGo); if (pointerEvent.pointerDrag != null) ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag); } // PointerUp notification if (data.ReleasedThisFrame()) { // Debug.Log("Executing pressup on: " + pointer.pointerPress); ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler); // Debug.Log("KeyCode: " + pointer.eventData.keyCode); // see if we mouse up on the same element that we clicked on... var pointerUpHandler = ExecuteEvents.GetEventHandler(currentOverGo); // PointerClick and Drop events if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick) { ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler); } else if (pointerEvent.pointerDrag != null && pointerEvent.dragging) { ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler); } pointerEvent.eligibleForClick = false; pointerEvent.pointerPress = null; pointerEvent.rawPointerPress = null; if (pointerEvent.pointerDrag != null && pointerEvent.dragging) ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler); pointerEvent.dragging = false; pointerEvent.pointerDrag = null; // redo pointer enter / exit to refresh state // so that if we moused over somethign that ignored it before // due to having pressed on something else // it now gets it. if (currentOverGo != pointerEvent.pointerEnter) { HandlePointerExitAndEnter(pointerEvent, null); HandlePointerExitAndEnter(pointerEvent, currentOverGo); } } } protected GameObject GetCurrentFocusedGameObject() { return m_CurrentFocusedGameObject; } } }