Skip to content

Instantly share code, notes, and snippets.

@AlexMerzlikin
Created December 23, 2019 15:47
Show Gist options
  • Save AlexMerzlikin/163d69592f6160f393ee056b4c62b743 to your computer and use it in GitHub Desktop.
Save AlexMerzlikin/163d69592f6160f393ee056b4c62b743 to your computer and use it in GitHub Desktop.
Multi-touch for Unity UI objects by Kogar https://forum.unity.com/threads/eventsystem.266410/#post-1782306
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace UnityEngine.EventSystems
{
public class MultiTouchInputModule : TouchInputModule
{
private PinchRotateEventData _pinchRotateData;
private const int notSelected = -2;
public int[] fingerIDs = {notSelected, notSelected};
private enum _MultiTouchMode
{
Idle, // no touch input.
Began, // 2 input touches received.
Pinching, // touches have passed the min pinch threshold
Rotating // touches have passed the min rotating threshold
}
private _MultiTouchMode _touchMode = _MultiTouchMode.Idle;
private Vector2 _prevVector = Vector2.zero;
private float _stationaryTime;
public float minRotationThreshold = 10;
public float minPinchThreshold = 10;
public float stationaryTimeThreshold = 1;
protected override void Start ()
{
_pinchRotateData = new PinchRotateEventData (eventSystem);
base.Start();
}
public bool isPointerUsed(int fingerID)
{
bool pressed, released;
Touch touch = Input.GetTouch(fingerID);
PointerEventData touchData = GetTouchPointerEventData (touch, out pressed, out released);
RaycastResult firstHit = touchData.pointerCurrentRaycast;
bool hasPointerDownEvent = ExecuteEvents.CanHandleEvent<IPointerDownHandler>(firstHit.go);
if(!hasPointerDownEvent)
return true;
return false;
}
public void select2Fingers()
{
Touch currentTouch;
for(int i=0; i<Input.touchCount; i++)
{
currentTouch = Input.GetTouch(i);
switch(currentTouch.phase)
{
case TouchPhase.Began:
if(!isPointerUsed(currentTouch.fingerId))
{
if(fingerIDs[0] == notSelected) //First finger for pinch/rotate
{
fingerIDs[0] = currentTouch.fingerId;
}
else if(fingerIDs[1] == notSelected) //second finger for pinch/rotate
{
fingerIDs[1] = currentTouch.fingerId;
}
}
break;
case TouchPhase.Ended:
if(fingerIDs[1] == currentTouch.fingerId)
{
fingerIDs[1] = notSelected;
} else if(fingerIDs[0] == currentTouch.fingerId)
{
fingerIDs[0] = fingerIDs[1]; //move up Maybe second finger gets placed again
fingerIDs[1] = notSelected;
}
break;
}
}
}
public override void Process ()
{
select2Fingers();
if (fingerIDs[0]!=notSelected && fingerIDs[1]!=notSelected) {
ProcessMove(GetLastPointerEventData(fingerIDs[0]));
ProcessMove(GetLastPointerEventData(fingerIDs[1]));
PointerEventData touchData0, touchData1;
Touch touch0, touch1;
bool pressed0, released0;
bool pressed1, released1;
touch0 = Input.GetTouch(fingerIDs[0]);
touch1 = Input.GetTouch(fingerIDs[1]);
touchData0 = GetTouchPointerEventData (touch0, out pressed0, out released0);
touchData1 = GetTouchPointerEventData (touch1, out pressed1, out released1);
RaycastResult firstHit0 = touchData0.pointerCurrentRaycast;
RaycastResult firstHit1 = touchData1.pointerCurrentRaycast;
if (touch0.phase == TouchPhase.Began || touch1.phase == TouchPhase.Began) {
_touchMode = _MultiTouchMode.Began;
_prevVector = touch1.position - touch0.position;
} else
if (touch0.phase == TouchPhase.Moved || touch1.phase == TouchPhase.Moved) {
if (firstHit0.go != null && firstHit1.go != null) {
if (firstHit0.go.Equals (firstHit1.go)) {
bool executeHandler = DetectMultiTouchMotion ();
if (executeHandler) {
_pinchRotateData.data [0] = touchData0;
_pinchRotateData.data [1] = touchData1;
if (_touchMode == _MultiTouchMode.Pinching) {
ExecuteEvents.Execute (firstHit0.go, _pinchRotateData, MultiTouchModuleEvents.pinchHandler);
} else if (_touchMode == _MultiTouchMode.Rotating) {
ExecuteEvents.Execute (firstHit0.go, _pinchRotateData, MultiTouchModuleEvents.rotateHandler);
}
}
}
}
}else
//check for ended or cancelled touched fingers and set the mode back to "idle".
if (touch0.phase == TouchPhase.Ended || touch1.phase == TouchPhase.Ended ||
touch0.phase == TouchPhase.Canceled || touch1.phase == TouchPhase.Canceled) {
_touchMode = _MultiTouchMode.Idle;
_prevVector = Vector2.zero;
}
//check for stationary fingers and set the mode back to "Began" if above the threshold.
if (touch0.phase == TouchPhase.Stationary || touch1.phase == TouchPhase.Stationary) {
_stationaryTime += Time.deltaTime;
if (_stationaryTime > stationaryTimeThreshold) {
_touchMode = _MultiTouchMode.Began;
_prevVector = touch1.position - touch0.position;
_stationaryTime = 0;
}
}
}
base.Process();
}
bool DetectMultiTouchMotion ()
{
Vector2 currentVector = Input.GetTouch(fingerIDs[1]).position - Input.GetTouch(fingerIDs[0]).position;
switch(_touchMode)
{
case _MultiTouchMode.Began:
{
//check for rotation threshold
float angleOffset = Vector2.Angle (_prevVector, currentVector);
if (angleOffset > minRotationThreshold) {
_touchMode = _MultiTouchMode.Rotating;
_prevVector = currentVector;
}
// check for pinch threshold
if (Mathf.Abs (currentVector.magnitude - _prevVector.magnitude) > minPinchThreshold) {
_touchMode = _MultiTouchMode.Pinching;
_prevVector = currentVector;
}
return false;
}
case _MultiTouchMode.Rotating:
{
float rotateDelta = Vector2.Angle (_prevVector, currentVector);
// to get the direction of rotation
Vector3 dirVec = Vector3.Cross (_prevVector, currentVector);
_prevVector = currentVector;
_pinchRotateData.rotateDelta = dirVec.z < 0 ? -rotateDelta : rotateDelta;
return true;
}
case _MultiTouchMode.Pinching:
{
float pinchDelta = currentVector.magnitude - _prevVector.magnitude;
_prevVector = currentVector;
_pinchRotateData.delta = pinchDelta;
return true;
}
}
return false;
}
public override string ToString ()
{
return string.Format ("[MultiTouchInputModule]");
}
}
public class PinchRotateEventData : PointerEventData
{
public PointerEventData[] data = new PointerEventData[2];
public float delta;
public float rotateDelta;
public PinchRotateEventData (EventSystem ES, float d = 0) : base (ES)
{
//delta = d;
}
}
public interface IPinchHandler : IEventSystemHandler
{
void OnPinch (PinchRotateEventData data);
}
public interface IRotateHandler : IEventSystemHandler
{
void OnRotate (PinchRotateEventData data);
}
public static class MultiTouchModuleEvents
{
private static void Execute (IPinchHandler handler, BaseEventData eventData)
{
handler.OnPinch (ExecuteEvents.ValidateEventData<PinchRotateEventData> (eventData));
}
private static void Execute (IRotateHandler handler, BaseEventData eventData)
{
handler.OnRotate (ExecuteEvents.ValidateEventData<PinchRotateEventData> (eventData));
}
public static ExecuteEvents.EventFunction<IPinchHandler> pinchHandler {
get { return Execute; }
}
public static ExecuteEvents.EventFunction<IRotateHandler> rotateHandler {
get { return Execute; }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment