Skip to content

Instantly share code, notes, and snippets.

@SolarianZ
Created June 11, 2024 04:32
Show Gist options
  • Save SolarianZ/e368613cffb5ef3d29ef8f35b15d1498 to your computer and use it in GitHub Desktop.
Save SolarianZ/e368613cffb5ef3d29ef8f35b15d1498 to your computer and use it in GitHub Desktop.
{"category": "Unity Engine/Runtime/Input", "keywords": "Unity, Runtime, Input, UI, Touch, Mouse, Drag, Zoom"} Detects touch screen or mouse input in the specified UI area.
using System;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UInput = UnityEngine.Input;
public interface IGraphicInputProvider
{
public Vector2 Drag { get; }
public float Zoom { get; }
void SetEnable(bool enable);
}
[DefaultExecutionOrder(-99)]
[DisallowMultipleComponent]
[RequireComponent(typeof(Graphic))] // Used to detects input area, works with IPointerXXXHandler
public class GraphicInput : MonoBehaviour, IGraphicInputProvider,
IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler
{
#region IGraphicInputProvider
public Vector2 Drag { get; private set; }
public float Zoom { get; private set; }
public void SetEnable(bool enable)
{
this.enabled = enable;
}
#endregion
#region Unity消息
private void OnEnable()
{
_touchSupported = UInput.touchSupported;
}
private void OnDisable()
{
_touchInputMode = TouchInputMode.None;
_captureMouseZoom = false;
_captureMouseDrag = false;
Drag = default;
Zoom = default;
}
private void Update()
{
if (DetectTouchInputMode())
{
UpdateTouchInput();
}
else
{
UpdateMouseInput();
}
}
#endregion
#region IPointerXXXHandler
void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
{
_captureMouseZoom = true;
}
void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
{
_captureMouseZoom = false;
}
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
_touchCount++;
_captureMouseDrag = true;
_lastMousePosition = eventData.position;
}
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
_touchCount--;
_captureMouseDrag = false;
Assert.IsTrue(_touchCount >= 0);
}
#endregion
#region Touch Input
private bool _touchSupported;
private int _touchCount;
private TouchInputMode _touchInputMode = TouchInputMode.None;
private Vector2 _lastPointer0Position;
private float _lastTwoPointersDistance;
private bool DetectTouchInputMode()
{
if (!_touchSupported)
{
return _touchInputMode != TouchInputMode.None;
}
TouchInputMode newTouchInputMode;
if (_touchCount == 0)
{
newTouchInputMode = TouchInputMode.None;
}
else
{
// The update timing of Input.touchCount and OnPointerDown is inconsistent,
// do not use the cached _touchCount here.
// https://forum.unity.com/threads/the-update-timing-of-input-touchcount-is-later-than-ipointeruphandler-onpointerup.1601727/
switch (UInput.touchCount)
{
case 0:
newTouchInputMode = TouchInputMode.None;
break;
case 1:
newTouchInputMode = TouchInputMode.Drag;
break;
default:
newTouchInputMode = TouchInputMode.Zoom;
break;
}
}
if (newTouchInputMode == _touchInputMode)
{
return _touchInputMode != TouchInputMode.None;
}
switch (newTouchInputMode)
{
case TouchInputMode.None:
ExitTouchInput();
break;
case TouchInputMode.Drag:
EnterTouchDragMode(UInput.mousePosition);
break;
case TouchInputMode.Zoom:
EnterTouchZoomMode();
break;
default:
throw new ArgumentOutOfRangeException();
}
return _touchInputMode != TouchInputMode.None;
}
private void ExitTouchInput()
{
Assert.IsTrue(UInput.touchCount == 0);
_touchInputMode = TouchInputMode.None;
Zoom = default;
Drag = default;
}
private void EnterTouchDragMode(Vector2 pointer0Position)
{
Assert.IsTrue(UInput.touchCount == 1);
_touchInputMode = TouchInputMode.Drag;
_lastPointer0Position = pointer0Position;
Drag = default;
Zoom = default;
}
private void EnterTouchZoomMode()
{
Assert.IsTrue(UInput.touchCount > 1);
_touchInputMode = TouchInputMode.Zoom;
Vector2 pointer0Pos = UInput.GetTouch(0).position;
Vector2 pointer1Pos = UInput.GetTouch(1).position;
_lastTwoPointersDistance = Vector2.Distance(pointer0Pos, pointer1Pos);
Drag = default;
Zoom = default;
}
private void UpdateTouchInput()
{
switch (_touchInputMode)
{
case TouchInputMode.None:
Zoom = default;
Drag = default;
break;
case TouchInputMode.Drag:
{
Vector2 pointer0Pos = UInput.GetTouch(0).position;
Drag = pointer0Pos - _lastPointer0Position;
_lastPointer0Position = pointer0Pos;
break;
}
case TouchInputMode.Zoom:
{
Vector2 pointer0Pos = UInput.GetTouch(0).position;
Vector2 pointer1Pos = UInput.GetTouch(1).position;
float twoPointersDist = Vector2.Distance(pointer0Pos, pointer1Pos);
float distanceDelta = twoPointersDist - _lastTwoPointersDistance;
_lastTwoPointersDistance = twoPointersDist;
Zoom = distanceDelta;
break;
}
default:
throw new ArgumentOutOfRangeException();
}
}
private enum TouchInputMode
{
None,
Drag,
Zoom,
}
#endregion
#region Mouse Input
private bool _captureMouseDrag;
private bool _captureMouseZoom;
private Vector2 _lastMousePosition;
private void UpdateMouseInput()
{
if (_captureMouseDrag)
{
// In the Editor, if the mouse clicks outside the Game window and then returns to the Game window
// to get Mouse X, Y, the values obtained will be very large, causing the viewport to jump
// float h = Input.GetAxis("Mouse X");
// float v = Input.GetAxis("Mouse Y");
// Drag = new Vector2(h, v);
Vector2 mousePosition = UInput.mousePosition;
Drag = mousePosition - _lastMousePosition;
_lastMousePosition = mousePosition;
}
else
{
Drag = default;
}
if (_captureMouseZoom)
{
Zoom = UInput.GetAxis("Mouse ScrollWheel");
}
else
{
Zoom = default;
}
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment