Created
December 23, 2019 15:47
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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