/*
* 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;
}
}
}