using Cryville.Common.Buffers; using UnityEngine; using UnityEngine.EventSystems; namespace Cryville.Unity.EventSystems { [AddComponentMenu("Event/GC-less Standalone Input Module")] public class StandaloneInputModule : UnityEngine.EventSystems.StandaloneInputModule { private bool ShouldIgnoreEventsOnNoFocus() { #if UNITY_EDITOR return !UnityEditor.EditorApplication.isRemoteConnected; #else return true; #endif } public override void Process() { if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus()) { return; } bool usedEvent = SendUpdateEventToSelectedObject(); if (!ProcessTouchEvents() && input.mousePresent) { ProcessMouseEvent(); } if (eventSystem.sendNavigationEvents) { if (!usedEvent) { usedEvent |= SendMoveEventToSelectedObject(); } if (!usedEvent) { SendSubmitEventToSelectedObject(); } } } private bool ProcessTouchEvents() { for (int i = 0; i < input.touchCount; i++) { Touch touch = input.GetTouch(i); if (touch.type != TouchType.Indirect) { PointerEventData pointer = GetTouchPointerEventData(touch, out bool pressed, out bool released); ProcessTouchPress(pointer, pressed, released); if (!released) { ProcessMove(pointer); ProcessDrag(pointer); } else { RemovePointerData(pointer); } } } return input.touchCount > 0; } protected new PointerEventData GetTouchPointerEventData(Touch input, out bool pressed, out bool released) { bool created = GetPointerData(input.fingerId, out PointerEventData pointerData, create: true); pointerData.Reset(); pressed = created || input.phase == TouchPhase.Began; released = input.phase == TouchPhase.Canceled || input.phase == TouchPhase.Ended; if (created) { pointerData.position = input.position; } if (pressed) { pointerData.delta = Vector2.zero; } else { pointerData.delta = input.position - pointerData.position; } pointerData.position = input.position; pointerData.button = PointerEventData.InputButton.Left; if (input.phase == TouchPhase.Canceled) { pointerData.pointerCurrentRaycast = default; } else { eventSystem.RaycastAll(pointerData, m_RaycastResultCache); pointerData.pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache); m_RaycastResultCache.Clear(); } pointerData.pressure = input.pressure; pointerData.altitudeAngle = input.altitudeAngle; pointerData.azimuthAngle = input.azimuthAngle; pointerData.radius = Vector2.one * input.radius; pointerData.radiusVariance = Vector2.one * input.radiusVariance; return pointerData; } class PointerEventDataPool : ObjectPool { readonly EventSystem _eventSystem; public PointerEventDataPool(EventSystem eventSystem, int capacity) : base(capacity) { _eventSystem = eventSystem; } protected override PointerEventData Construct() { return new PointerEventData(_eventSystem); } } PointerEventDataPool _pool; protected override void OnEnable() { base.OnEnable(); _pool = new PointerEventDataPool(eventSystem, 64); } protected new bool GetPointerData(int id, out PointerEventData data, bool create) { if (!m_PointerData.TryGetValue(id, out data) && create) { data = _pool.Rent(); data.pointerId = id; m_PointerData.Add(id, data); return true; } return false; } protected new void RemovePointerData(PointerEventData data) { m_PointerData.Remove(data.pointerId); _pool.Return(data); } } }