Unity AR 实现模型交互

一、背景介绍

在 AR 应用开发中,模型的交互体验是非常重要的用户体验部分。用户希望能够用手指去旋转、缩放、拖动模型,就像真实地摆弄实物一样。

Unity 虽然提供了 Input 系统,但要自己写完整的多点触控逻辑(多指缩放、旋转等)较为繁琐。为了更高效地实现手势控制,我学习并使用了 EasyTouch 插件,并将其应用在我的 Unity AR 项目中,实现了模型的自然交互。

我使用的是EasyTouch 5。

二、AR 项目整体架构

整体流程:

  1. 使用 Vuforia 识别不同的 ImageTarget;

  2. 每个 ImageTarget 绑定对应的 3D 模型;

  3. 在模型上挂载 EasyTouch 交互逻辑,实现用户手势控制;

  4. 实现了模型旋转、缩放、双击等功能。

三、在 Unity 中集成 EasyTouch

EasyTouch > Add EasyTouch to Scene。

这一步会在场景中自动创建一个 EasyTouch GameObject,作为全局手势监听器。

注意:对于每个模型下添加 BoxCollider 或者其他碰撞体组件(必须有 Collider 供射线检测)

cs 复制代码
/***********************************************
				EasyTouch V
	Copyright © 2014-2015 The Hedgehog Team
    http://www.thehedgehogteam.com/Forum/
		
	  The.Hedgehog.Team@gmail.com
		
**********************************************/

using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;

namespace HedgehogTeam.EasyTouch{
/// <summary>
/// This is the main class of Easytouch engine. 
/// 
/// For add Easy Touch to your scene
/// It is a fake singleton, so you can simply access their settings via a script with all static methods or with the inspector.<br>
/// </summary>
public class EasyTouch : MonoBehaviour {

	#region private classes
	[System.Serializable]
	private class DoubleTap{
		public bool inDoubleTap = false;
		public bool inWait = false;
		public float time=0;
		public int count=0;
		public Finger finger;

		public void Stop(){
			inDoubleTap = false;
			inWait = false;
			time=0;
			count=0;
		}
	}	

	private class PickedObject{
		public GameObject pickedObj;
		public Camera pickedCamera;
		public bool isGUI;
	}
	#endregion

	#region Delegate
	public delegate void TouchCancelHandler(Gesture gesture);
	public delegate void Cancel2FingersHandler(Gesture gesture);
	public delegate void TouchStartHandler(Gesture gesture);
	public delegate void TouchDownHandler(Gesture gesture);
	public delegate void TouchUpHandler(Gesture gesture);
	public delegate void SimpleTapHandler(Gesture gesture);
	public delegate void DoubleTapHandler(Gesture gesture);
	public delegate void LongTapStartHandler(Gesture gesture);
	public delegate void LongTapHandler(Gesture gesture);
	public delegate void LongTapEndHandler(Gesture gesture);
	public delegate void DragStartHandler(Gesture gesture);
	public delegate void DragHandler(Gesture gesture);
	public delegate void DragEndHandler(Gesture gesture);
	public delegate void SwipeStartHandler(Gesture gesture);
	public delegate void SwipeHandler(Gesture gesture);
	public delegate void SwipeEndHandler(Gesture gesture);
	public delegate void TouchStart2FingersHandler(Gesture gesture);
	public delegate void TouchDown2FingersHandler(Gesture gesture);
	public delegate void TouchUp2FingersHandler(Gesture gesture);
	public delegate void SimpleTap2FingersHandler(Gesture gesture);
	public delegate void DoubleTap2FingersHandler(Gesture gesture);
	public delegate void LongTapStart2FingersHandler(Gesture gesture);
	public delegate void LongTap2FingersHandler(Gesture gesture);
	public delegate void LongTapEnd2FingersHandler(Gesture gesture);
	public delegate void TwistHandler(Gesture gesture);
	public delegate void TwistEndHandler(Gesture gesture);
	public delegate void PinchInHandler(Gesture gesture);
	public delegate void PinchOutHandler(Gesture gesture);
	public delegate void PinchEndHandler(Gesture gesture);
	public delegate void PinchHandler(Gesture gesture);
	public delegate void DragStart2FingersHandler(Gesture gesture);
	public delegate void Drag2FingersHandler(Gesture gesture);
	public delegate void DragEnd2FingersHandler(Gesture gesture);
	public delegate void SwipeStart2FingersHandler(Gesture gesture);
	public delegate void Swipe2FingersHandler(Gesture gesture);
	public delegate void SwipeEnd2FingersHandler(Gesture gesture);
	public delegate void EasyTouchIsReadyHandler();

	public delegate void OverUIElementHandler( Gesture gesture);
	public delegate void UIElementTouchUpHandler( Gesture gesture);
	#endregion
	
	#region Events
	/// <summary>
	/// Occurs when The system cancelled tracking for the touch, as when (for example) the user puts the device to her face.
	/// </summary>
	public static event TouchCancelHandler On_Cancel;
	/// <summary>
	/// Occurs when the touch count is no longer egal to 2 and different to 0, after the begining of a two fingers gesture.
	/// </summary>
	public static event Cancel2FingersHandler On_Cancel2Fingers;
	/// <summary>
	/// Occurs when a finger touched the screen.
	/// </summary>
	public static event TouchStartHandler On_TouchStart;
	/// <summary>
	/// Occurs as the touch is active.
	/// </summary>
	public static event TouchDownHandler On_TouchDown;
	/// <summary>
	/// Occurs when a finger was lifted from the screen.
	/// </summary>
	public static event TouchUpHandler On_TouchUp;
	/// <summary>
	/// Occurs when a finger was lifted from the screen, and the time elapsed since the beginning of the touch is less than the time required for the detection of a long tap.
	/// </summary>
	public static event SimpleTapHandler On_SimpleTap;
	/// <summary>
	/// Occurs when the number of taps is egal to 2 in a short time.
	/// </summary>
	public static event DoubleTapHandler On_DoubleTap;
	/// <summary>
	/// Occurs when a finger is touching the screen,  but hasn't moved  since the time required for the detection of a long tap.
	/// </summary>
	public static event LongTapStartHandler On_LongTapStart;
	/// <summary>
	/// Occurs as the touch is active after a LongTapStart
	/// </summary>
	public static event LongTapHandler On_LongTap;
	/// <summary>
	/// Occurs when a finger was lifted from the screen, and the time elapsed since the beginning of the touch is more than the time required for the detection of a long tap.
	/// </summary>
	public static event LongTapEndHandler On_LongTapEnd;
	/// <summary>
	/// Occurs when a drag start. A drag is a swipe on a pickable object
	/// </summary>
	public static event DragStartHandler On_DragStart;
	/// <summary>
	/// Occurs as the drag is active.
	/// </summary>
	public static event DragHandler On_Drag;
	/// <summary>
	/// Occurs when a finger that raise the drag event , is lifted from the screen.
	/// </summary>/
	public static event DragEndHandler On_DragEnd;
	/// <summary>
	/// Occurs when swipe start.
	/// </summary>
	public static event SwipeStartHandler On_SwipeStart;
	/// <summary>
	/// Occurs as the  swipe is active.
	/// </summary>
	public static event SwipeHandler On_Swipe;
	/// <summary>
	/// Occurs when a finger that raise the swipe event , is lifted from the screen.
	/// </summary>
	public static event SwipeEndHandler On_SwipeEnd;
	/// <summary>
	/// Like On_TouchStart but for a 2 fingers gesture.
	/// </summary>
	public static event TouchStart2FingersHandler On_TouchStart2Fingers;
	/// <summary>
	/// Like On_TouchDown but for a 2 fingers gesture.
	/// </summary>
	public static event TouchDown2FingersHandler On_TouchDown2Fingers;
	/// <summary>
	/// Like On_TouchUp but for a 2 fingers gesture.
	/// </summary>
	public static event TouchUp2FingersHandler On_TouchUp2Fingers;
	/// <summary>
	/// Like On_SimpleTap but for a 2 fingers gesture.
	/// </summary>
	public static event SimpleTap2FingersHandler On_SimpleTap2Fingers;
	/// <summary>
	/// Like On_DoubleTap but for a 2 fingers gesture.
	/// </summary>
	public static event DoubleTap2FingersHandler On_DoubleTap2Fingers;
	/// <summary>
	/// Like On_LongTapStart but for a 2 fingers gesture.
	/// </summary>
	public static event LongTapStart2FingersHandler On_LongTapStart2Fingers;
	/// <summary>
	/// Like On_LongTap but for a 2 fingers gesture.
	/// </summary>
	public static event LongTap2FingersHandler On_LongTap2Fingers;
	/// <summary>
	/// Like On_LongTapEnd but for a 2 fingers gesture.
	/// </summary>
	public static event LongTapEnd2FingersHandler On_LongTapEnd2Fingers;
	/// <summary>
	/// Occurs when a twist gesture start
	/// </summary>
	public static event TwistHandler On_Twist;
	/// <summary>
	/// Occurs as the twist gesture is active.
	/// </summary>
	public static event TwistEndHandler On_TwistEnd;
	/// <summary>
	/// Occurs as the pinch  gesture is active.
	/// </summary>
	public static event PinchHandler On_Pinch;
	/// <summary>
	/// Occurs as the pinch in gesture is active.
	/// </summary>
	public static event PinchInHandler On_PinchIn;
	/// <summary>
	/// Occurs as the pinch out gesture is active.
	/// </summary>
	public static event PinchOutHandler On_PinchOut;
	/// <summary>
	/// Occurs when the 2 fingers that raise the pinch event , are lifted from the screen.
	/// </summary>
	public static event PinchEndHandler On_PinchEnd;
	/// <summary>
	/// Like On_DragStart but for a 2 fingers gesture.
	/// </summary>
	public static event DragStart2FingersHandler On_DragStart2Fingers;
	/// <summary>
	/// Like On_Drag but for a 2 fingers gesture.
	/// </summary>
	public static event Drag2FingersHandler On_Drag2Fingers;
	/// <summary>
	/// Like On_DragEnd2Fingers but for a 2 fingers gesture.
	/// </summary>
	public static event DragEnd2FingersHandler On_DragEnd2Fingers;
	/// <summary>
	/// Like On_SwipeStart but for a 2 fingers gesture.
	/// </summary>
	public static event SwipeStart2FingersHandler On_SwipeStart2Fingers;
	/// <summary>
	/// Like On_Swipe but for a 2 fingers gesture.
	/// </summary>
	public static event Swipe2FingersHandler On_Swipe2Fingers;
	/// <summary>
	/// Like On_SwipeEnd but for a 2 fingers gesture.
	/// </summary>
	public static event SwipeEnd2FingersHandler On_SwipeEnd2Fingers;
	/// <summary>
	/// Occurs when  easy touch is ready.
	/// </summary>
	public static event EasyTouchIsReadyHandler On_EasyTouchIsReady;
	/// <summary>
	/// Occurs when current touch is over user interface element.
	/// </summary>
	public static event OverUIElementHandler On_OverUIElement;

	public static event UIElementTouchUpHandler On_UIElementTouchUp;
	#endregion

	#region Enumerations

	public enum GesturePriority{ Tap, Slips};

	public enum DoubleTapDetection { BySystem, ByTime}

	public enum GestureType{ Tap, Drag, Swipe, None, LongTap, Pinch, Twist, Cancel, Acquisition };
	/// <summary>
	/// Represents the different directions for a swipe or drag gesture (Left, Right, Up, Down, Other)
	/// 
	/// The direction is influenced by the swipe Tolerance parameter Look at SetSwipeTolerance( float tolerance)
	/// <br><br>
	/// This enumeration is used on Gesture class
	/// </summary>
	public enum SwipeDirection{ None, Left, Right, Up, Down, UpLeft, UpRight, DownLeft, DownRight,Other,All};
		
	public enum TwoFingerPickMethod{ Finger, Average};
	
	public enum EvtType{ None,On_TouchStart,On_TouchDown,On_TouchUp,On_SimpleTap,On_DoubleTap,On_LongTapStart,On_LongTap,
	On_LongTapEnd,On_DragStart,On_Drag,On_DragEnd,On_SwipeStart,On_Swipe,On_SwipeEnd,On_TouchStart2Fingers,On_TouchDown2Fingers,On_TouchUp2Fingers,On_SimpleTap2Fingers,
	On_DoubleTap2Fingers,On_LongTapStart2Fingers,On_LongTap2Fingers,On_LongTapEnd2Fingers,On_Twist,On_TwistEnd,On_Pinch,On_PinchIn,On_PinchOut,On_PinchEnd,On_DragStart2Fingers,
		On_Drag2Fingers,On_DragEnd2Fingers,On_SwipeStart2Fingers,On_Swipe2Fingers,On_SwipeEnd2Fingers, On_EasyTouchIsReady ,On_Cancel, On_Cancel2Fingers,On_OverUIElement, On_UIElementTouchUp}

	#endregion
	
	#region Public members
	private static EasyTouch _instance;
	public static EasyTouch instance{
		get{
			if( !_instance ){
				
				// check if an ObjectPoolManager is already available in the scene graph
				_instance = FindObjectOfType( typeof( EasyTouch ) ) as EasyTouch;
				
				// nope, create a new one
				if( !_instance ){
					GameObject obj = new GameObject( "Easytouch" );
					_instance = obj.AddComponent<EasyTouch>();
				}
			}
			
			return _instance;
		}
	}

	private Gesture _currentGesture = new Gesture();
	public static Gesture current{
		get{
			return EasyTouch.instance._currentGesture;
		}
	}

	private List<Gesture> _currentGestures = new List<Gesture>();

	public bool enable;				// Enables or disables Easy Touch
	public bool enableRemote;			// Enables or disables Unity remote
		
	// General gesture properties
	public GesturePriority gesturePriority; 
	public float StationaryTolerance;// 
	public float longTapTime ;			// The time required for the detection of a long tap.
	public float swipeTolerance;		// Determines the accuracy of detecting a drag movement 0 => no precision 1=> high precision.
	public float minPinchLength;			// The minimum length for a pinch detection.
	public float minTwistAngle;			// The minimum angle for a twist detection.
	public DoubleTapDetection doubleTapDetection;
	public float doubleTapTime;
	public bool alwaysSendSwipe;
	//public bool isDpi;

	// Two finger gesture
	public bool enable2FingersGesture; // Enables 2 fingers gesture.
	public bool enableTwist;			// Enables or disables recognition of the twist
	public bool enablePinch;			// Enables or disables recognition of the Pinch
	public bool enable2FingersSwipe; 	// Enables or disables recognition of 2 fingers swipe
	public TwoFingerPickMethod twoFingerPickMethod;

	// Auto selection
	public List<ECamera> touchCameras;	// The  cameras
	public bool autoSelect;  							// Enables or disables auto select
	public LayerMask pickableLayers3D;							// Layer detectable by default
	public bool enable2D;								// Enables or disables auto select on 2D
	public LayerMask pickableLayers2D;		
	public bool autoUpdatePickedObject;

	// Unity UI
	//public EasyTouchRaycaster uiRaycaster;
	public bool allowUIDetection;
	public bool enableUIMode;
	public bool autoUpdatePickedUI;

	// NGUI
	public bool enabledNGuiMode;	// True = no events are send when touch is hover an NGui panel
	public LayerMask nGUILayers;
	public List<Camera> nGUICameras;
		
	// Second Finger
	public bool enableSimulation;
	public KeyCode twistKey;
	public KeyCode swipeKey;

	// Inspector
	public bool showGuiInspector = false;
	public bool showSelectInspector = false;
	public bool showGestureInspector = false;
	public bool showTwoFingerInspector = false;
	public bool showSecondFingerInspector = false;
	#endregion
	
	#region Private members	
	private EasyTouchInput input = new EasyTouchInput();
	private Finger[] fingers=new Finger[100];					// The informations of the touch for finger 1.
	public Texture secondFingerTexture;							// The texture to display the simulation of the second finger.
	private TwoFingerGesture twoFinger = new TwoFingerGesture();
	private int oldTouchCount=0;
	private DoubleTap[] singleDoubleTap = new DoubleTap[100];
	private Finger[] tmpArray = new Finger[100];
	private PickedObject pickedObject = new PickedObject();

	// Unity UI
	private List<RaycastResult> uiRaycastResultCache= new List<RaycastResult>();
	private PointerEventData uiPointerEventData;
	private EventSystem uiEventSystem;

	#endregion
	
	#region Constructor
	public EasyTouch(){
		enable = true;		

		allowUIDetection = true;
		enableUIMode = true;
		autoUpdatePickedUI = false;

		enabledNGuiMode = false;
		nGUICameras = new List<Camera>();

		autoSelect = true; 
		touchCameras = new List<ECamera>();
		pickableLayers3D = 1<<0;
		enable2D = false;
		pickableLayers2D = 1<<0;

		gesturePriority = GesturePriority.Tap;
		StationaryTolerance = 15;
		longTapTime =1;
		doubleTapDetection = DoubleTapDetection.BySystem;
		doubleTapTime = 0.3f;
		swipeTolerance = 0.85f;
		alwaysSendSwipe = false;

		enable2FingersGesture=true; 
		twoFingerPickMethod = TwoFingerPickMethod.Finger;
		enable2FingersSwipe = true;
		enablePinch = true;
		minPinchLength = 0f;
		enableTwist = true;
		minTwistAngle = 0f;

		enableSimulation = true;
		twistKey = KeyCode.LeftAlt;
		swipeKey = KeyCode.LeftControl;

		
	}
	#endregion
	
	#region MonoBehaviour Callback
	void OnEnable(){
		if (Application.isPlaying && Application.isEditor){
			Init();	
		}
	}

	void Awake(){
		Init();	
	}

	void Start(){

		for (int i=0;i<100;i++){
			singleDoubleTap[i] = new DoubleTap();
		}
		int index = touchCameras.FindIndex( 
			delegate(ECamera c){
				return c.camera == Camera.main;
			}
		);
		
		if (index<0)
			touchCameras.Add(new ECamera(Camera.main,false));

		// Fire ready event
		if (On_EasyTouchIsReady!=null){
			On_EasyTouchIsReady();	
		}

		// Current gesture
		_currentGestures.Add( new Gesture());
	}
	
	void Init(){

		// The texture to display the simulation of the second finger.
		#if ((!UNITY_ANDROID && !UNITY_IOS &&  !UNITY_TVOS && !UNITY_WINRT && !UNITY_BLACKBERRY) || UNITY_EDITOR)
			if (secondFingerTexture==null && enableSimulation){
				secondFingerTexture =Resources.Load("secondFinger") as Texture;
			}
		#endif	
	}
	
	// Display the simulation of the second finger
	#if ((!UNITY_ANDROID && !UNITY_IOS && !UNITY_TVOS && !UNITY_WINRT && !UNITY_BLACKBERRY) || UNITY_EDITOR) 
	void OnGUI(){
		if (enableSimulation && !enableRemote){
			Vector2 finger = input.GetSecondFingerPosition();
			if (finger!=new Vector2(-1,-1)){		
				GUI.DrawTexture( new Rect(finger.x-16,Screen.height-finger.y-16,32,32),secondFingerTexture);
			}
		}
	}
	#endif
	
	void OnDrawGizmos(){
	}

	// Non comments.
	void Update(){
	

		if (enable && EasyTouch.instance==this){

			//#if (UNITY_EDITOR )
			if (Application.isPlaying && Input.touchCount>0){
				enableRemote = true;
			}
			
			if (Application.isPlaying && Input.touchCount==0){
				enableRemote = false;
			}
			//#endif

			int i;
			
			// How many finger do we have ?
			int count = input.TouchCount();
		
			// Reset after two finger gesture;
			if (oldTouchCount==2 && count!=2 && count>0){
				CreateGesture2Finger(EvtType.On_Cancel2Fingers,Vector2.zero,Vector2.zero,Vector2.zero,0,SwipeDirection.None,0,Vector2.zero,0,0,0);
			}

			// Get touches		
			//#if (((UNITY_ANDROID || UNITY_IOS || UNITY_WINRT || UNITY_BLACKBERRY || UNITY_TVOS) && !UNITY_EDITOR))
				#if (((UNITY_ANDROID || UNITY_IOS || UNITY_BLACKBERRY || UNITY_TVOS || UNITY_PSP2) && !UNITY_EDITOR))
				UpdateTouches(true, count);
			#else
				UpdateTouches(false, count);
			#endif				
		
			// two fingers gesture
			twoFinger.oldPickedObject = twoFinger.pickedObject;
			if (enable2FingersGesture){
				if (count==2){
					TwoFinger();
				}
			}

			// Other fingers gesture
			for (i=0;i<100;i++){
				if (fingers[i]!=null){
					OneFinger(i);
				}
			}

			oldTouchCount = count;
		}
	}


	void LateUpdate(){

		// single gesture
		if (_currentGestures.Count>1){
			_currentGestures.RemoveAt(0);	
		}
		else{
         //       _currentGestures[0] = null;// new Gesture();
		}
		_currentGesture = _currentGestures[0];


	}

		
 
	void UpdateTouches(bool realTouch, int touchCount){
		 
		fingers.CopyTo( tmpArray,0);
		
		
		if (realTouch || enableRemote){
			ResetTouches();
			for (var i = 0; i < touchCount; ++i) {
				Touch touch = Input.GetTouch(i);
				
				int t=0;
				while (t < 100 && fingers[i]==null){	
					if (tmpArray[t] != null){
						if ( tmpArray[t].fingerIndex == touch.fingerId){								
							fingers[i] = tmpArray[t];
						}
					}
					t++;	
				}
				
				if (fingers[i]==null){
					fingers[i]= new Finger();
					fingers[i].fingerIndex = touch.fingerId;
					fingers[i].gesture = GestureType.None;
					fingers[i].phase = TouchPhase.Began;
				}
				else{
					fingers[i].phase = touch.phase;
				}

				if ( fingers[i].phase!= TouchPhase.Began){
					fingers[i].deltaPosition = touch.position - fingers[i].position;
				}
				else{
					fingers[i].deltaPosition = Vector2.zero;
				}

				fingers[i].position = touch.position;
				//fingers[i].deltaPosition = touch.deltaPosition;
				fingers[i].tapCount = touch.tapCount;
				fingers[i].deltaTime = touch.deltaTime;
				
				fingers[i].touchCount = touchCount;		

				fingers[i].altitudeAngle = touch.altitudeAngle;
				fingers[i].azimuthAngle = touch.azimuthAngle;
				fingers[i].maximumPossiblePressure = touch.maximumPossiblePressure;
				fingers[i].pressure = touch.pressure;
				fingers[i].radius = touch.radius;
				fingers[i].radiusVariance = touch.radiusVariance;
				fingers[i].touchType = touch.type;

			}
		}
		else{
			int i=0;
			while (i<touchCount){
				fingers[i] = input.GetMouseTouch(i,fingers[i]) as Finger;
				fingers[i].touchCount = touchCount;
				i++;
			}			
		}
		
	}
	
	void ResetTouches(){
		for (int i=0;i<100;i++){
			fingers[i] = null;
		}	
	}	
	#endregion
	
	#region One finger Private methods
	private void OneFinger(int fingerIndex){

		// A tap starts ?
		if ( fingers[fingerIndex].gesture==GestureType.None){

			if (!singleDoubleTap[fingerIndex].inDoubleTap){
				singleDoubleTap[fingerIndex].inDoubleTap = true;
				singleDoubleTap[fingerIndex].time = 0;
				singleDoubleTap[fingerIndex].count = 1;
			}

			fingers[fingerIndex].startTimeAction = Time.realtimeSinceStartup;
			fingers[fingerIndex].gesture=GestureType.Acquisition;
			fingers[fingerIndex].startPosition = fingers[fingerIndex].position;
			
			// do we touch a pickable gameobject ?
			if (autoSelect){
				if (GetPickedGameObject(fingers[fingerIndex])){
					fingers[fingerIndex].pickedObject = pickedObject.pickedObj;
					fingers[fingerIndex].isGuiCamera = pickedObject.isGUI;
					fingers[fingerIndex].pickedCamera = pickedObject.pickedCamera;
				}
			}

			// UnityGUI
			if (allowUIDetection){
				fingers[fingerIndex].isOverGui = IsScreenPositionOverUI(  fingers[fingerIndex].position );
				fingers[fingerIndex].pickedUIElement = GetFirstUIElementFromCache();
			}

			// we notify a touch
			CreateGesture(fingerIndex, EvtType.On_TouchStart,fingers[fingerIndex], SwipeDirection.None,0,Vector2.zero);
		}

		if (singleDoubleTap[fingerIndex].inDoubleTap) singleDoubleTap[fingerIndex].time += Time.deltaTime;
		
		// Calculates the time since the beginning of the action.
		fingers[fingerIndex].actionTime =  Time.realtimeSinceStartup - fingers[fingerIndex].startTimeAction;
		
		
		// touch canceled?
		if (fingers[fingerIndex].phase == TouchPhase.Canceled){
			fingers[fingerIndex].gesture = GestureType.Cancel;
		}
		
		if (fingers[fingerIndex].phase != TouchPhase.Ended && fingers[fingerIndex].phase != TouchPhase.Canceled){
		
			// Are we stationary  for a long tap
			if (fingers[fingerIndex].phase == TouchPhase.Stationary  && 
			    fingers[fingerIndex].actionTime >= longTapTime && fingers[fingerIndex].gesture == GestureType.Acquisition){

				fingers[fingerIndex].gesture = GestureType.LongTap;				
				CreateGesture(fingerIndex, EvtType.On_LongTapStart,fingers[fingerIndex], SwipeDirection.None,0,Vector2.zero);	
			}
			
			// Let's move us?
			if (( (fingers[fingerIndex].gesture == GestureType.Acquisition ||fingers[fingerIndex].gesture == GestureType.LongTap) && fingers[fingerIndex].phase == TouchPhase.Moved && gesturePriority == GesturePriority.Slips)
				|| ((fingers[fingerIndex].gesture == GestureType.Acquisition ||fingers[fingerIndex].gesture == GestureType.LongTap) && (FingerInTolerance(fingers[fingerIndex])==false) && gesturePriority == GesturePriority.Tap ))
			{

				//  long touch => cancel
				if (fingers[fingerIndex].gesture == GestureType.LongTap){
					fingers[fingerIndex].gesture = GestureType.Cancel;
					CreateGesture(fingerIndex, EvtType.On_LongTapEnd,fingers[fingerIndex],SwipeDirection.None,0,Vector2.zero);
					// Init the touch to start
					fingers[fingerIndex].gesture=GestureType.Acquisition;	
				}
				else{
					fingers[fingerIndex].oldSwipeType = SwipeDirection.None;

					// If an object is selected we drag
					if (fingers[fingerIndex].pickedObject){
						fingers[fingerIndex].gesture = GestureType.Drag;
						CreateGesture(fingerIndex, EvtType.On_DragStart,fingers[fingerIndex],SwipeDirection.None,0, Vector2.zero);

						if (alwaysSendSwipe){
							CreateGesture(fingerIndex, EvtType.On_SwipeStart,fingers[fingerIndex], SwipeDirection.None,0,Vector2.zero);
						}
					}
					// If not swipe
					else{
						fingers[fingerIndex].gesture = GestureType.Swipe;
						CreateGesture(fingerIndex, EvtType.On_SwipeStart,fingers[fingerIndex], SwipeDirection.None,0,Vector2.zero);
					}
					
				}
			}
			
			// Gesture update
			EvtType message = EvtType.None;
			
			switch (fingers[fingerIndex].gesture){
				case GestureType.LongTap:
					message=EvtType.On_LongTap;
					break;
				case GestureType.Drag:
					message=EvtType.On_Drag;
					break;
				case GestureType.Swipe:
					message=EvtType.On_Swipe;
					break;
			}
			
			// Send gesture
			SwipeDirection currentSwipe = SwipeDirection.None;
			currentSwipe = GetSwipe(new Vector2(0,0),fingers[fingerIndex].deltaPosition);
			if (message!=EvtType.None){

				fingers[fingerIndex].oldSwipeType = currentSwipe;
				CreateGesture(fingerIndex, message,fingers[fingerIndex], currentSwipe ,0,fingers[fingerIndex].deltaPosition);

				if (message ==  EvtType.On_Drag && alwaysSendSwipe){
					CreateGesture(fingerIndex, EvtType.On_Swipe,fingers[fingerIndex], currentSwipe ,0,fingers[fingerIndex].deltaPosition);
				}
			}
			
			// TouchDown
			CreateGesture(fingerIndex, EvtType.On_TouchDown,fingers[fingerIndex], currentSwipe,0,fingers[fingerIndex].deltaPosition);
		}
		else{
			// End of the touch		
			switch (fingers[fingerIndex].gesture){
				// tap
				case GestureType.Acquisition:
					
					if (doubleTapDetection == DoubleTapDetection.BySystem){
						if (FingerInTolerance(fingers[fingerIndex])){
							if (fingers[fingerIndex].tapCount<2){
								CreateGesture( fingerIndex, EvtType.On_SimpleTap,fingers[fingerIndex], SwipeDirection.None,0,Vector2.zero);
							}
							else{
								CreateGesture( fingerIndex, EvtType.On_DoubleTap,fingers[fingerIndex],  SwipeDirection.None,0,Vector2.zero);
							}
							
						}
					}
					else{
						if (!singleDoubleTap[fingerIndex].inWait){
							singleDoubleTap[fingerIndex].finger = fingers[fingerIndex];
							StartCoroutine(SingleOrDouble(fingerIndex) );
						}
						else{
							singleDoubleTap[fingerIndex].count++;
						}
					}
	
					break;
				// long tap
				case GestureType.LongTap:
					CreateGesture( fingerIndex, EvtType.On_LongTapEnd,fingers[fingerIndex], SwipeDirection.None,0,Vector2.zero);
					break;
				// drag
				case GestureType.Drag:
					CreateGesture(fingerIndex,  EvtType.On_DragEnd,fingers[fingerIndex], GetSwipe(fingers[fingerIndex].startPosition,fingers[fingerIndex].position), (fingers[fingerIndex].startPosition-fingers[fingerIndex].position).magnitude,fingers[fingerIndex].position-fingers[fingerIndex].startPosition);
					if (alwaysSendSwipe){
						CreateGesture( fingerIndex, EvtType.On_SwipeEnd,fingers[fingerIndex], GetSwipe(fingers[fingerIndex].startPosition, fingers[fingerIndex].position), (fingers[fingerIndex].position-fingers[fingerIndex].startPosition).magnitude,fingers[fingerIndex].position-fingers[fingerIndex].startPosition); 
					}
					break;
				// swipe
				case GestureType.Swipe:
					CreateGesture( fingerIndex, EvtType.On_SwipeEnd,fingers[fingerIndex], GetSwipe(fingers[fingerIndex].startPosition, fingers[fingerIndex].position), (fingers[fingerIndex].position-fingers[fingerIndex].startPosition).magnitude,fingers[fingerIndex].position-fingers[fingerIndex].startPosition); 
					break;

				// cancel
				case GestureType.Cancel:
					CreateGesture(fingerIndex, EvtType.On_Cancel,fingers[fingerIndex],SwipeDirection.None,0,Vector2.zero);
					break;

			}
			CreateGesture( fingerIndex, EvtType.On_TouchUp,fingers[fingerIndex], SwipeDirection.None,0,Vector2.zero);
			fingers[fingerIndex]=null;		

		}
	
	}

	IEnumerator SingleOrDouble(int fingerIndex){
		singleDoubleTap[fingerIndex].inWait = true;
		float time2Wait = doubleTapTime-singleDoubleTap[fingerIndex].finger.actionTime;
		if (time2Wait<0) time2Wait =0;
		yield return new WaitForSeconds(time2Wait);


		if (singleDoubleTap[fingerIndex].count <2){
			//CreateGesture( fingerIndex, EvtType.On_SimpleTap,singleDoubleTap[fingerIndex].finger, SwipeDirection.None,0,Vector2.zero);
			CreateGesture( fingerIndex, EvtType.On_SimpleTap,singleDoubleTap[fingerIndex].finger, SwipeDirection.None,0,singleDoubleTap[fingerIndex].finger.deltaPosition);
		}
		else{
			//CreateGesture( fingerIndex, EvtType.On_DoubleTap,singleDoubleTap[fingerIndex].finger, SwipeDirection.None,0,Vector2.zero);
			CreateGesture( fingerIndex, EvtType.On_DoubleTap,singleDoubleTap[fingerIndex].finger, SwipeDirection.None,0,singleDoubleTap[fingerIndex].finger.deltaPosition);
		}

		//fingers[fingerIndex]=null;
		singleDoubleTap[fingerIndex].Stop();
		StopCoroutine( "SingleOrDouble");
	}

	private void CreateGesture(int touchIndex,EvtType message,Finger finger, SwipeDirection swipe, float swipeLength, Vector2 swipeVector){

		bool firingEvent = true;

		if (autoUpdatePickedUI && allowUIDetection){
			finger.isOverGui = IsScreenPositionOverUI( finger.position );
			finger.pickedUIElement = GetFirstUIElementFromCache();
		}

		// NGui
		if (enabledNGuiMode  && message == EvtType.On_TouchStart){
			finger.isOverGui = finger.isOverGui || IsTouchOverNGui(finger.position);
		}

		// firing event ?
		if ((enableUIMode || enabledNGuiMode)){
			firingEvent = !finger.isOverGui;
		}

		// The new gesture
		Gesture gesture = finger.GetGesture();

		// Auto update picked object
		if (autoUpdatePickedObject && autoSelect){
			if (message != EvtType.On_Drag && message != EvtType.On_DragEnd && message != EvtType.On_DragStart){
				if (GetPickedGameObject(finger)){
					gesture.pickedObject = pickedObject.pickedObj;
					gesture.pickedCamera = pickedObject.pickedCamera;
					gesture.isGuiCamera = pickedObject.isGUI;
				}
				else{
					gesture.pickedObject = null;
					gesture.pickedCamera = null;
					gesture.isGuiCamera = false;
				}
			}
		}

		gesture.swipe = swipe;
		gesture.swipeLength = swipeLength;
		gesture.swipeVector = swipeVector;

		gesture.deltaPinch = 0;
		gesture.twistAngle = 0;


		// Firing event
		if ( firingEvent){
			RaiseEvent(message, gesture);
		}
		else if (finger.isOverGui){
			if (message == EvtType.On_TouchUp){
				RaiseEvent(EvtType.On_UIElementTouchUp, gesture);
			}
			else{
				RaiseEvent(EvtType.On_OverUIElement, gesture);
			}
		}

	}	
	#endregion

	#region Two finger private methods
	private void TwoFinger(){

		bool move=false;
				
		// A touch starts
		if ( twoFinger.currentGesture==GestureType.None){

			if (!singleDoubleTap[99].inDoubleTap){
				singleDoubleTap[99].inDoubleTap = true;
				singleDoubleTap[99].time = 0;
				singleDoubleTap[99].count = 1;
			}

			twoFinger.finger0 = GetTwoFinger(-1);
			twoFinger.finger1 = GetTwoFinger(twoFinger.finger0);
			
			twoFinger.startTimeAction = Time.realtimeSinceStartup;
			twoFinger.currentGesture=GestureType.Acquisition;			

			fingers[twoFinger.finger0].startPosition = fingers[twoFinger.finger0].position;
			fingers[twoFinger.finger1].startPosition = fingers[twoFinger.finger1].position;

			fingers[twoFinger.finger0].oldPosition = fingers[twoFinger.finger0].position;
			fingers[twoFinger.finger1].oldPosition = fingers[twoFinger.finger1].position;
			
			
			twoFinger.oldFingerDistance = Mathf.Abs( Vector2.Distance(fingers[twoFinger.finger0].position, fingers[twoFinger.finger1].position));
			twoFinger.startPosition = new Vector2((fingers[twoFinger.finger0].position.x+fingers[twoFinger.finger1].position.x)/2, (fingers[twoFinger.finger0].position.y+fingers[twoFinger.finger1].position.y)/2);
			twoFinger.position = twoFinger.startPosition;
			twoFinger.oldStartPosition = twoFinger.startPosition;
			twoFinger.deltaPosition = Vector2.zero;
			twoFinger.startDistance = twoFinger.oldFingerDistance;

			// do we touch a pickable gameobject ?
			if (autoSelect){
				if (GetTwoFingerPickedObject()){
					twoFinger.pickedObject = pickedObject.pickedObj;
					twoFinger.pickedCamera = pickedObject.pickedCamera;
					twoFinger.isGuiCamera = pickedObject.isGUI;
				}
				else{
					twoFinger.ClearPickedObjectData();
				}
			}

			// UnityGUI
			if (allowUIDetection){
				if (GetTwoFingerPickedUIElement()){
					twoFinger.pickedUIElement = pickedObject.pickedObj;
					twoFinger.isOverGui = true;
				}
				else{
					twoFinger.ClearPickedUIData();
				}
			}

			// we notify the touch
			CreateGesture2Finger(EvtType.On_TouchStart2Fingers,twoFinger.startPosition,twoFinger.startPosition,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, SwipeDirection.None,0,Vector2.zero,0,0,twoFinger.oldFingerDistance);				
		}

		if (singleDoubleTap[99].inDoubleTap) singleDoubleTap[99].time += Time.deltaTime;

		// Calculates the time since the beginning of the action.
		twoFinger.timeSinceStartAction =  Time.realtimeSinceStartup -twoFinger.startTimeAction;
		
		// Position & deltaPosition
		twoFinger.position = new  Vector2((fingers[twoFinger.finger0].position.x+fingers[twoFinger.finger1].position.x)/2, (fingers[twoFinger.finger0].position.y+fingers[twoFinger.finger1].position.y)/2);
		twoFinger.deltaPosition = twoFinger.position - twoFinger.oldStartPosition;
		twoFinger.fingerDistance = Mathf.Abs(Vector2.Distance(fingers[twoFinger.finger0].position, fingers[twoFinger.finger1].position));
		
		// Cancel
		if (fingers[twoFinger.finger0].phase == TouchPhase.Canceled ||fingers[twoFinger.finger1].phase == TouchPhase.Canceled){
			twoFinger.currentGesture = GestureType.Cancel;
		}

		// Let's go
		if (fingers[twoFinger.finger0].phase != TouchPhase.Ended && fingers[twoFinger.finger1].phase != TouchPhase.Ended && twoFinger.currentGesture != GestureType.Cancel){


			// Are we stationary ?
			if (twoFinger.currentGesture == GestureType.Acquisition && twoFinger.timeSinceStartAction >= longTapTime && FingerInTolerance(fingers[twoFinger.finger0]) && FingerInTolerance(fingers[twoFinger.finger1])){	
				twoFinger.currentGesture = GestureType.LongTap;				
				// we notify the beginning of a longtouch
				CreateGesture2Finger(EvtType.On_LongTapStart2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, SwipeDirection.None,0,Vector2.zero,0,0,twoFinger.fingerDistance);				
			}	
			
			// Let's move us ?
			if ( ((FingerInTolerance(fingers[twoFinger.finger0])==false || FingerInTolerance(fingers[twoFinger.finger1])==false) && gesturePriority == GesturePriority.Tap ) 
			|| ((fingers[twoFinger.finger0].phase == TouchPhase.Moved || fingers[twoFinger.finger1].phase == TouchPhase.Moved) && gesturePriority == GesturePriority.Slips) ) {
				move=true;
			}

			// we move
			if (move && twoFinger.currentGesture != GestureType.Tap){
				Vector2 currentDistance = fingers[twoFinger.finger0].position - fingers[twoFinger.finger1].position;
				Vector2 previousDistance = fingers[twoFinger.finger0].oldPosition - fingers[twoFinger.finger1].oldPosition ;
				float currentDelta = currentDistance.magnitude - previousDistance.magnitude;


				#region drag & swipe
				if (enable2FingersSwipe){
					float dot = Vector2.Dot(fingers[twoFinger.finger0].deltaPosition.normalized, fingers[twoFinger.finger1].deltaPosition.normalized);

					if (dot>0 ){

						if (twoFinger.oldGesture == GestureType.LongTap){
							CreateStateEnd2Fingers(twoFinger.currentGesture,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction,false,twoFinger.fingerDistance);
							twoFinger.startTimeAction = Time.realtimeSinceStartup;
						}

						if (twoFinger.pickedObject && !twoFinger.dragStart && !alwaysSendSwipe){

							twoFinger.currentGesture = GestureType.Drag;

							CreateGesture2Finger(EvtType.On_DragStart2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, SwipeDirection.None,0,Vector2.zero,0,0,twoFinger.fingerDistance);	
							CreateGesture2Finger(EvtType.On_SwipeStart2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, SwipeDirection.None,0,Vector2.zero,0,0,twoFinger.fingerDistance);	
							twoFinger.dragStart = true; 
						}
						else if (!twoFinger.pickedObject && !twoFinger.swipeStart){

							twoFinger.currentGesture = GestureType.Swipe;

							CreateGesture2Finger(EvtType.On_SwipeStart2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, SwipeDirection.None,0,Vector2.zero,0,0,twoFinger.fingerDistance);
							twoFinger.swipeStart=true;
						}

					} 
					else{
						if (dot<0){
							twoFinger.dragStart=false; 
							twoFinger.swipeStart=false;
						}
					}

					//
					if (twoFinger.dragStart){
						CreateGesture2Finger(EvtType.On_Drag2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, GetSwipe(twoFinger.oldStartPosition,twoFinger.position),0,twoFinger.deltaPosition,0,0,twoFinger.fingerDistance);
						CreateGesture2Finger(EvtType.On_Swipe2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, GetSwipe(twoFinger.oldStartPosition,twoFinger.position),0,twoFinger.deltaPosition,0,0,twoFinger.fingerDistance);
					}
					
					if (twoFinger.swipeStart){
						CreateGesture2Finger(EvtType.On_Swipe2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, GetSwipe(twoFinger.oldStartPosition,twoFinger.position),0,twoFinger.deltaPosition,0,0,twoFinger.fingerDistance);
					}

				}
				#endregion

				DetectPinch(currentDelta);
				DetecTwist( previousDistance, currentDistance,currentDelta);
			}
			else{
				// Long tap update
				if (twoFinger.currentGesture == GestureType.LongTap){
					CreateGesture2Finger(EvtType.On_LongTap2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, SwipeDirection.None,0,Vector2.zero,0,0,twoFinger.fingerDistance);
				}	
			}

			CreateGesture2Finger(EvtType.On_TouchDown2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, GetSwipe(twoFinger.oldStartPosition,twoFinger.position),0,twoFinger.deltaPosition,0,0,twoFinger.fingerDistance);

			fingers[twoFinger.finger0].oldPosition = fingers[twoFinger.finger0].position;
			fingers[twoFinger.finger1].oldPosition = fingers[twoFinger.finger1].position;

			twoFinger.oldFingerDistance = twoFinger.fingerDistance;
			twoFinger.oldStartPosition = twoFinger.position;
			twoFinger.oldGesture = twoFinger.currentGesture;

		}
		else{			

			if (twoFinger.currentGesture != GestureType.Acquisition && twoFinger.currentGesture!= GestureType.Tap){
				CreateStateEnd2Fingers(twoFinger.currentGesture,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction,true,twoFinger.fingerDistance);
				twoFinger.currentGesture = GestureType.None;
				twoFinger.pickedObject=null;
				twoFinger.swipeStart = false;
				twoFinger.dragStart = false;

			}
			else{
				twoFinger.currentGesture = GestureType.Tap;
				CreateStateEnd2Fingers(twoFinger.currentGesture,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction,true,twoFinger.fingerDistance);
			}
		}	
	}




	private void DetectPinch(float currentDelta){
		#region Pinch
		if (enablePinch){

			if ((Mathf.Abs(twoFinger.fingerDistance - twoFinger.startDistance)>= minPinchLength && twoFinger.currentGesture != GestureType.Pinch) || twoFinger.currentGesture== GestureType.Pinch ){

				if (currentDelta !=0 && twoFinger.oldGesture == GestureType.LongTap){
					CreateStateEnd2Fingers(twoFinger.currentGesture,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction,false,twoFinger.fingerDistance);
					twoFinger.startTimeAction = Time.realtimeSinceStartup;
				}
				
				twoFinger.currentGesture = GestureType.Pinch;
				
				if (currentDelta>0){
					CreateGesture2Finger(EvtType.On_PinchOut,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, GetSwipe(twoFinger.startPosition,twoFinger.position),0,Vector2.zero,0,Mathf.Abs(twoFinger.fingerDistance-twoFinger.oldFingerDistance),twoFinger.fingerDistance);
				}
				
				if (currentDelta<0){
					CreateGesture2Finger(EvtType.On_PinchIn,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, GetSwipe(twoFinger.startPosition,twoFinger.position),0,Vector2.zero,0,Mathf.Abs(twoFinger.fingerDistance-twoFinger.oldFingerDistance),twoFinger.fingerDistance);
				}
				
				if (currentDelta<0 || currentDelta>0){
					CreateGesture2Finger(EvtType.On_Pinch,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, GetSwipe(twoFinger.startPosition,twoFinger.position),0,Vector2.zero,0,currentDelta,twoFinger.fingerDistance);
				}

			}

			twoFinger.lastPinch = currentDelta>0?currentDelta:twoFinger.lastPinch;
		}
		#endregion
	}
	
	private void DetecTwist(Vector2 previousDistance, Vector2 currentDistance, float currentDelta){
		#region Twist
		if (enableTwist){

			float twistAngle = Vector2.Angle( previousDistance, currentDistance );

			//Debug.Log( twistAngle);
			if (previousDistance == currentDistance)
				twistAngle =0;

			if ( Mathf.Abs(twistAngle)>=minTwistAngle && (twoFinger.currentGesture != GestureType.Twist ) || twoFinger.currentGesture== GestureType.Twist ){
				
				if ( twoFinger.oldGesture == GestureType.LongTap){
					CreateStateEnd2Fingers(twoFinger.currentGesture,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction,false,twoFinger.fingerDistance);
					twoFinger.startTimeAction = Time.realtimeSinceStartup;
				}
				
				twoFinger.currentGesture = GestureType.Twist;
				
				if (twistAngle!=0){
					twistAngle *= Mathf.Sign( Vector3.Cross( previousDistance,  currentDistance).z);
				}
				
				CreateGesture2Finger(EvtType.On_Twist,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,twoFinger.timeSinceStartAction, SwipeDirection.None,0,Vector2.zero,twistAngle,0,twoFinger.fingerDistance);
			}

			twoFinger.lastTwistAngle = twistAngle!=0?twistAngle:twoFinger.lastTwistAngle;
		}			
		#endregion
	}


	private void CreateStateEnd2Fingers(GestureType gesture, Vector2 startPosition, Vector2 position, Vector2 deltaPosition,float time, bool realEnd,float fingerDistance,float twist=0, float pinch=0){


		switch (gesture){
			// Tap
		case GestureType.Tap:
		case GestureType.Acquisition:

			if (doubleTapDetection == DoubleTapDetection.BySystem){

				if (fingers[twoFinger.finger0].tapCount<2 && fingers[twoFinger.finger1].tapCount<2){
					CreateGesture2Finger(EvtType.On_SimpleTap2Fingers,startPosition,position,deltaPosition,
					                     time, SwipeDirection.None,0,Vector2.zero,0,0,fingerDistance);				
				}
				else{
					CreateGesture2Finger(EvtType.On_DoubleTap2Fingers,startPosition,position,deltaPosition,
					                     time, SwipeDirection.None,0,Vector2.zero,0,0,fingerDistance);
				}
				twoFinger.currentGesture = GestureType.None;
				twoFinger.pickedObject=null;
				twoFinger.swipeStart = false;
				twoFinger.dragStart = false;
				singleDoubleTap[99].Stop();
				StopCoroutine( "SingleOrDouble2Fingers");

			}
			else{
				if (!singleDoubleTap[99].inWait){
					StartCoroutine("SingleOrDouble2Fingers" );
				}
				else{
					singleDoubleTap[99].count++;
				}
			}
			break;
			
			// Long tap
		case GestureType.LongTap:
			CreateGesture2Finger(EvtType.On_LongTapEnd2Fingers,startPosition,position,deltaPosition,
			                     time, SwipeDirection.None,0,Vector2.zero,0,0,fingerDistance);
			break;
			
			// Pinch 
		case GestureType.Pinch:
			CreateGesture2Finger(EvtType.On_PinchEnd,startPosition,position,deltaPosition,
			                     time, SwipeDirection.None,0,Vector2.zero,0,twoFinger.lastPinch,fingerDistance);
			break;
			
			// twist
		case GestureType.Twist:
			CreateGesture2Finger(EvtType.On_TwistEnd,startPosition,position,deltaPosition,
			                     time, SwipeDirection.None,0,Vector2.zero,twoFinger.lastTwistAngle,0,fingerDistance);
			break;	
		}
		
		if (realEnd){
			// Drag
			if ( twoFinger.dragStart){
				CreateGesture2Finger(EvtType.On_DragEnd2Fingers,startPosition,position,deltaPosition,
				                     time, GetSwipe( startPosition, position),( position-startPosition).magnitude,position-startPosition,0,0,fingerDistance);
			};
			
			// Swipe
			if ( twoFinger.swipeStart){
				CreateGesture2Finger(EvtType.On_SwipeEnd2Fingers,startPosition,position,deltaPosition,
				                     time, GetSwipe( startPosition, position),( position-startPosition).magnitude,position-startPosition,0,0,fingerDistance);
			}
			
			CreateGesture2Finger(EvtType.On_TouchUp2Fingers,startPosition,position,deltaPosition,time, SwipeDirection.None,0,Vector2.zero,0,0,fingerDistance);
		}
	}

	IEnumerator SingleOrDouble2Fingers(){
		singleDoubleTap[99].inWait = true;

		yield return new WaitForSeconds(doubleTapTime);

		if (singleDoubleTap[99].count <2){

			CreateGesture2Finger(EvtType.On_SimpleTap2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,
			                     twoFinger.timeSinceStartAction, SwipeDirection.None,0,Vector2.zero,0,0,twoFinger.fingerDistance);

		}
		else{
			CreateGesture2Finger(EvtType.On_DoubleTap2Fingers,twoFinger.startPosition,twoFinger.position,twoFinger.deltaPosition,
			                     twoFinger.timeSinceStartAction, SwipeDirection.None,0,Vector2.zero,0,0,twoFinger.fingerDistance);
		}

		twoFinger.currentGesture = GestureType.None;
		twoFinger.pickedObject=null;
		twoFinger.swipeStart = false;
		twoFinger.dragStart = false;
		singleDoubleTap[99].Stop();
		StopCoroutine( "SingleOrDouble2Fingers");
	}

	private void  CreateGesture2Finger(EvtType message,Vector2 startPosition,Vector2 position,Vector2 deltaPosition,
	                                   float actionTime, SwipeDirection swipe, float swipeLength,Vector2 swipeVector,float twist,float pinch, float twoDistance){

		bool firingEvent = true;
		Gesture gesture = new Gesture();
		gesture.isOverGui = false;

		// NGui
		if (enabledNGuiMode && message == EvtType.On_TouchStart2Fingers){
			gesture.isOverGui = gesture.isOverGui || ( IsTouchOverNGui(twoFinger.position) && IsTouchOverNGui(twoFinger.position));
		}

		gesture.touchCount=2;
		gesture.fingerIndex=-1;
		gesture.startPosition = startPosition;	
		gesture.position = position;
		gesture.deltaPosition = deltaPosition;
		
		gesture.actionTime = actionTime;
		gesture.deltaTime=Time.deltaTime;
		
		gesture.swipe = swipe;
		gesture.swipeLength = swipeLength;
		gesture.swipeVector = swipeVector;
		
		gesture.deltaPinch = pinch;
		gesture.twistAngle = twist;
		gesture.twoFingerDistance = twoDistance;

		gesture.pickedObject = twoFinger.pickedObject;
		gesture.pickedCamera = twoFinger.pickedCamera;
		gesture.isGuiCamera= twoFinger.isGuiCamera;

		if (autoUpdatePickedObject){
			if (message != EvtType.On_Drag && message != EvtType.On_DragEnd && message != EvtType.On_Twist && message != EvtType.On_TwistEnd && message != EvtType.On_Pinch && message != EvtType.On_PinchEnd
			 && message != EvtType.On_PinchIn && message != EvtType.On_PinchOut){

				if (GetTwoFingerPickedObject()){
					gesture.pickedObject = pickedObject.pickedObj;
					gesture.pickedCamera = pickedObject.pickedCamera;
					gesture.isGuiCamera= pickedObject.isGUI;
				}
				else{
					twoFinger.ClearPickedObjectData();
				}
			}
		}

		gesture.pickedUIElement = twoFinger.pickedUIElement;
		gesture.isOverGui = twoFinger.isOverGui;


		if (allowUIDetection && autoUpdatePickedUI){
			if (message != EvtType.On_Drag && message != EvtType.On_DragEnd && message != EvtType.On_Twist && message != EvtType.On_TwistEnd && message != EvtType.On_Pinch && message != EvtType.On_PinchEnd
			    && message != EvtType.On_PinchIn && message != EvtType.On_PinchOut){
				if (message == EvtType.On_SimpleTap2Fingers)

				if (GetTwoFingerPickedUIElement()){
					gesture.pickedUIElement = pickedObject.pickedObj;
					gesture.isOverGui = true;
				}
				else{
					twoFinger.ClearPickedUIData();
				}
			}
		}



		// Firing event ?
		if ((enableUIMode || (enabledNGuiMode && allowUIDetection) ) ){
			firingEvent = !gesture.isOverGui;
		}

		// Firing event
		if ( firingEvent){
			RaiseEvent(message, gesture);
		}
		else if (gesture.isOverGui){
			if (message == EvtType.On_TouchUp2Fingers){
				RaiseEvent(EvtType.On_UIElementTouchUp, gesture);
			}
			else{
				RaiseEvent(EvtType.On_OverUIElement, gesture);
			}
		}
	}

	private int GetTwoFinger( int index){
		
		int i=index+1;
		bool find=false;
		
		while (i<10 && !find){
			if (fingers[i]!=null ){
				if( i>=index){
					find=true;
				}
			}
			i++;
		}
		i--;
		
		return i;
	}

	private bool GetTwoFingerPickedObject(){

		bool returnValue = false;

		if (twoFingerPickMethod == TwoFingerPickMethod.Finger){
			if (GetPickedGameObject(fingers[twoFinger.finger0],false)){
				GameObject tmp = pickedObject.pickedObj;
				if (GetPickedGameObject(fingers[twoFinger.finger1],false)){
					if (tmp == pickedObject.pickedObj){
						returnValue = true;
					}
				}
			}
		}

		else{
			if (GetPickedGameObject(fingers[twoFinger.finger0],true)){
				returnValue = true;
			}
		}

		return returnValue;
	}

	private bool GetTwoFingerPickedUIElement(){

		bool returnValue = false;

		if (fingers[twoFinger.finger0] == null){
			return false;
		}

		if (twoFingerPickMethod == TwoFingerPickMethod.Finger){
			if (IsScreenPositionOverUI( fingers[twoFinger.finger0].position )){
				GameObject tmp = GetFirstUIElementFromCache();

				if (IsScreenPositionOverUI( fingers[twoFinger.finger1].position )){
					GameObject tmp2 = GetFirstUIElementFromCache();

					if (tmp2 == tmp || tmp2.transform.IsChildOf( tmp.transform) || tmp.transform.IsChildOf( tmp2.transform)){
						pickedObject.pickedObj = tmp;
						pickedObject.isGUI = true;
						returnValue = true;
					}
				}
			}
		}
		else{
			if (IsScreenPositionOverUI( twoFinger.position )){
				pickedObject.pickedObj = GetFirstUIElementFromCache();
				pickedObject.isGUI = true;
				returnValue = true;
			}
		}

		return returnValue;
	}

	#endregion

	#region General private methods
	private void RaiseEvent(EvtType evnt, Gesture gesture){
				
		gesture.type = evnt;

		switch(evnt){
			case EvtType.On_Cancel:
				if (On_Cancel!=null)
					On_Cancel( gesture);
				break;
			case EvtType.On_Cancel2Fingers:
				if (On_Cancel2Fingers!=null)
					On_Cancel2Fingers( gesture );
				break;
			case EvtType.On_TouchStart:
				if (On_TouchStart!=null)
					On_TouchStart( gesture);
				break;
			case EvtType.On_TouchDown:
				if (On_TouchDown!=null)
					On_TouchDown( gesture);
				break;
			case EvtType.On_TouchUp:
				if (On_TouchUp!=null)
					On_TouchUp( gesture );
				break;
			case EvtType.On_SimpleTap:
				if (On_SimpleTap!=null)
					On_SimpleTap( gesture);
				break;
			case EvtType.On_DoubleTap:
				if (On_DoubleTap!=null)
					On_DoubleTap(gesture);
				break;
			case EvtType.On_LongTapStart:
				if (On_LongTapStart!=null)
					On_LongTapStart(gesture);
				break;
			case EvtType.On_LongTap:
				if (On_LongTap!=null)
					On_LongTap(gesture);
				break;
			case EvtType.On_LongTapEnd:
				if (On_LongTapEnd!=null)
					On_LongTapEnd(gesture);
				break;
			case EvtType.On_DragStart:
				if (On_DragStart!=null)
					On_DragStart(gesture);
				break;
			case EvtType.On_Drag:
				if (On_Drag!=null)
					On_Drag(gesture);
				break;
			case EvtType.On_DragEnd:
				if (On_DragEnd!=null)
					On_DragEnd(gesture);
				break;
			case EvtType.On_SwipeStart:
				if (On_SwipeStart!=null)
					On_SwipeStart( gesture);
				break;
			case EvtType.On_Swipe:
				if (On_Swipe!=null)
					On_Swipe( gesture);
				break;
			case EvtType.On_SwipeEnd:
				if (On_SwipeEnd!=null)
					On_SwipeEnd(gesture);
				break;
			case EvtType.On_TouchStart2Fingers:
				if (On_TouchStart2Fingers!=null)
					On_TouchStart2Fingers( gesture);
				break;
			case EvtType.On_TouchDown2Fingers:
				if (On_TouchDown2Fingers!=null)
					On_TouchDown2Fingers(gesture);
				break;
			case EvtType.On_TouchUp2Fingers:
				if (On_TouchUp2Fingers!=null)
					On_TouchUp2Fingers(gesture);
				break;
			case EvtType.On_SimpleTap2Fingers:
				if (On_SimpleTap2Fingers!=null)
					On_SimpleTap2Fingers(gesture);
				break;
			case EvtType.On_DoubleTap2Fingers:
				if (On_DoubleTap2Fingers!=null)
					On_DoubleTap2Fingers(gesture);
				break;
			case EvtType.On_LongTapStart2Fingers:
				if (On_LongTapStart2Fingers!=null)
					On_LongTapStart2Fingers(gesture);
				break;
			case EvtType.On_LongTap2Fingers:
				if (On_LongTap2Fingers!=null)
					On_LongTap2Fingers(gesture);
				break;
			case EvtType.On_LongTapEnd2Fingers:
				if (On_LongTapEnd2Fingers!=null)
					On_LongTapEnd2Fingers(gesture);
				break;
			case EvtType.On_Twist:
				if (On_Twist!=null)
					On_Twist(gesture);
				break;
			case EvtType.On_TwistEnd:
				if (On_TwistEnd!=null)
					On_TwistEnd(gesture);
				break;
			case EvtType.On_Pinch:
				if (On_Pinch!=null)
					On_Pinch(gesture);
				break;
			case EvtType.On_PinchIn:
				if (On_PinchIn!=null)
					On_PinchIn(gesture);
				break;
			case EvtType.On_PinchOut:
				if (On_PinchOut!=null)
					On_PinchOut(gesture);
				break;
			case EvtType.On_PinchEnd:
				if (On_PinchEnd!=null)
					On_PinchEnd(gesture);
				break;
			case EvtType.On_DragStart2Fingers:
				if (On_DragStart2Fingers!=null)
					On_DragStart2Fingers(gesture);
				break;
			case EvtType.On_Drag2Fingers:
				if (On_Drag2Fingers!=null)
					On_Drag2Fingers(gesture);
				break;
			case EvtType.On_DragEnd2Fingers:
				if (On_DragEnd2Fingers!=null)
					On_DragEnd2Fingers(gesture);
				break;
			case EvtType.On_SwipeStart2Fingers:
				if (On_SwipeStart2Fingers!=null)
					On_SwipeStart2Fingers(gesture);
				break;
			case EvtType.On_Swipe2Fingers:
				if (On_Swipe2Fingers!=null)
					On_Swipe2Fingers(gesture);
				break;
			case EvtType.On_SwipeEnd2Fingers:
				if (On_SwipeEnd2Fingers!=null)
					On_SwipeEnd2Fingers(gesture);
				break;
			case EvtType.On_OverUIElement:
				if (On_OverUIElement!=null){
					On_OverUIElement(gesture);
				}
				break;
			case EvtType.On_UIElementTouchUp:
				if (On_UIElementTouchUp!=null){
					On_UIElementTouchUp( gesture);
				}
				break;
		}

		// Direct Acces 
		int result = _currentGestures.FindIndex( delegate(Gesture obj) {
			return obj!=null && obj.type == gesture.type && obj.fingerIndex == gesture.fingerIndex;
		}
		);

		if (result>-1){
			_currentGestures[result].touchCount = gesture.touchCount;
			_currentGestures[result].position = gesture.position;
			_currentGestures[result].actionTime = gesture.actionTime;
			_currentGestures[result].pickedCamera = gesture.pickedCamera;
			_currentGestures[result].pickedObject = gesture.pickedObject;
			_currentGestures[result].pickedUIElement = gesture.pickedUIElement;
			_currentGestures[result].isOverGui = gesture.isOverGui;
			_currentGestures[result].isGuiCamera = gesture.isGuiCamera;

			// Update delta from current
			_currentGestures[result].deltaPinch += gesture.deltaPinch;
			_currentGestures[result].deltaPosition += gesture.deltaPosition;

			_currentGestures[result].deltaTime += gesture.deltaTime;
			_currentGestures[result].twistAngle += gesture.twistAngle;
		}

		if (result==-1 ){
			_currentGestures.Add( (Gesture)gesture.Clone());
			if (_currentGestures.Count>0){
				_currentGesture = _currentGestures[0];
			}
		}


	}

	private bool GetPickedGameObject(Finger finger, bool isTowFinger=false){

		if (finger == null && !isTowFinger){
			return false;
		}

		pickedObject.isGUI = false;
		pickedObject.pickedObj = null;
		pickedObject.pickedCamera = null;
		
		if (touchCameras.Count>0){
			for (int i=0;i<touchCameras.Count;i++){
				if (touchCameras[i].camera!=null && touchCameras[i].camera.enabled){

					Vector2 pos=Vector2.zero;
					if (!isTowFinger){
						pos = finger.position;
					}
					else{
						pos = twoFinger.position;
					}

					
					if (GetGameObjectAt( pos, touchCameras[i].camera,touchCameras[i].guiCamera)){
							return true;
						}
					/*
			        Ray ray = touchCameras[i].camera.ScreenPointToRay( pos );
			        RaycastHit hit;

					if (enable2D){

						LayerMask mask2d = pickableLayers2D;
						RaycastHit2D[] hit2D = new RaycastHit2D[1];
						if (Physics2D.GetRayIntersectionNonAlloc( ray,hit2D,float.PositiveInfinity,mask2d)>0){
							pickedObject.pickedCamera = touchCameras[i].camera;
							pickedObject.isGUI = touchCameras[i].guiCamera;
							pickedObject.pickedObj = hit2D[0].collider.gameObject;
							return true;
						}
					}

					LayerMask mask = pickableLayers3D;
						
			        if( Physics.Raycast( ray, out hit,float.MaxValue,mask ) ){
						pickedObject.pickedCamera = touchCameras[i].camera;
						pickedObject.isGUI = touchCameras[i].guiCamera;
						pickedObject.pickedObj = hit.collider.gameObject;
			            return true;
					}*/
				}
			}
		}
		else{
			Debug.LogWarning("No camera is assigned to EasyTouch");	
		}
        return false;     
	}

	private bool GetGameObjectAt(Vector2 position, Camera cam, bool isGuiCam){

		Ray ray = cam.ScreenPointToRay( position );
		RaycastHit hit;

		if (enable2D){
			
			LayerMask mask2d = pickableLayers2D;
			RaycastHit2D[] hit2D = new RaycastHit2D[1];
			if (Physics2D.GetRayIntersectionNonAlloc( ray,hit2D,float.PositiveInfinity,mask2d)>0){
				pickedObject.pickedCamera = cam;
				pickedObject.isGUI = isGuiCam;
				pickedObject.pickedObj = hit2D[0].collider.gameObject;
				return true;
			}
		}
		
		LayerMask mask = pickableLayers3D;
		
		if( Physics.Raycast( ray, out hit,float.MaxValue,mask ) ){
			pickedObject.pickedCamera = cam;
			pickedObject.isGUI =isGuiCam;
			pickedObject.pickedObj = hit.collider.gameObject;
			return true;
		}

		return false;
	}

	private SwipeDirection GetSwipe(Vector2 start, Vector2 end){
		
		Vector2 linear;
		linear = (end - start).normalized;

		if ( Vector2.Dot( linear, Vector2.up) >= swipeTolerance)
			return SwipeDirection.Up;

		if ( Vector2.Dot( linear, -Vector2.up) >= swipeTolerance)
			return SwipeDirection.Down;

		if ( Vector2.Dot( linear,  Vector2.right) >= swipeTolerance)
			return SwipeDirection.Right;

		if ( Vector2.Dot( linear,  -Vector2.right) >= swipeTolerance)
			return SwipeDirection.Left;

		if ( Vector2.Dot( linear, new Vector2(0.5f,0.5f).normalized) >= swipeTolerance)
			return SwipeDirection.UpRight;

		if ( Vector2.Dot( linear, new Vector2(0.5f,-0.5f).normalized) >= swipeTolerance)
			return SwipeDirection.DownRight;
			
		if ( Vector2.Dot( linear, new Vector2(-0.5f,0.5f).normalized) >= swipeTolerance)
			return SwipeDirection.UpLeft;

		if ( Vector2.Dot( linear, new Vector2(-0.5f,-0.5f).normalized) >= swipeTolerance)
			return SwipeDirection.DownLeft;

		return SwipeDirection.Other;			
	}

	private bool FingerInTolerance(Finger finger ){

		if ((finger.position-finger.startPosition).sqrMagnitude <= (StationaryTolerance*StationaryTolerance)){
			return true;
		}
		else{
			return false;
		}
	}

	private bool IsTouchOverNGui(Vector2 position, bool isTwoFingers=false){
		
		bool returnValue = false;
		
		if (enabledNGuiMode){
			
			LayerMask mask= nGUILayers;
			RaycastHit hit;
								
			int i=0;
			while (!returnValue && i<nGUICameras.Count){
				Vector2 pos = Vector2.zero;
				if (!isTwoFingers){
					pos = position;//fingers[touchIndex].position;
				}
				else{
					pos = twoFinger.position;
				}
				Ray ray = nGUICameras[i].ScreenPointToRay( pos );

				returnValue =  Physics.Raycast( ray, out hit,float.MaxValue,mask );
				i++;
			}

		}

		return returnValue;
	
	}

	private Finger GetFinger(int finderId){
		int t=0;
		
		Finger fing=null;
		
		while (t < 10 && fing==null){	
			if (fingers[t] != null ){
				if ( fingers[t].fingerIndex == finderId){				
					fing = fingers[t];		
				}
			}
			t++;	
		}	
		
		return fing;
	}
	#endregion

	#region Unity UI
	private bool IsScreenPositionOverUI( Vector2 position){

		uiEventSystem = EventSystem.current;
		if (uiEventSystem != null){

			uiPointerEventData = new PointerEventData( uiEventSystem);
			uiPointerEventData.position = position;

			uiEventSystem.RaycastAll( uiPointerEventData, uiRaycastResultCache);
			if (uiRaycastResultCache.Count>0){
				return true;
			}
			else{
				return false;
			}
		}
		else{
			return false;
		}
	}

	private GameObject GetFirstUIElementFromCache(){

		if (uiRaycastResultCache.Count>0){
			return uiRaycastResultCache[0].gameObject;
		}
		else{
			return null;
		}
	}
	                                               
	private GameObject GetFirstUIElement( Vector2 position){

		if (IsScreenPositionOverUI( position)){
			return GetFirstUIElementFromCache();
		}
		else{
			return null;
		}
	}
	#endregion

	#region Static Method
	// Unity UI compatibility
	public static bool IsFingerOverUIElement( int fingerIndex){
		if (EasyTouch.instance!=null){
			Finger finger = EasyTouch.instance.GetFinger(fingerIndex);
			if (finger != null){
				return EasyTouch.instance.IsScreenPositionOverUI( finger.position);
			}
			else{
				return false;
			}
		}
		else{
			return false;
		}
	}
	
	public static GameObject GetCurrentPickedUIElement( int fingerIndex,bool isTwoFinger){
		if (EasyTouch.instance!=null){
			Finger finger = EasyTouch.instance.GetFinger(fingerIndex);
			if (finger != null || isTwoFinger){
				Vector2 pos = Vector2.zero;
				if (!isTwoFinger){
					pos = finger.position;
				}
				else{
					pos = EasyTouch.instance.twoFinger.position;
				}
				return EasyTouch.instance.GetFirstUIElement( pos);
			}
			else{
				return null;
			}
		}
		else{
			return null;
		}
	}

	public static GameObject GetCurrentPickedObject(int fingerIndex, bool isTwoFinger){

		if (EasyTouch.instance!=null){
			Finger finger = EasyTouch.instance.GetFinger(fingerIndex);

			if ((finger!=null || isTwoFinger) && EasyTouch.instance.GetPickedGameObject(finger,isTwoFinger)){
				return EasyTouch.instance.pickedObject.pickedObj;
			}
			else{
				return null;
			}
		}
		else{
			return null;
		}
		
	}

	public static GameObject GetGameObjectAt( Vector2 position, bool isTwoFinger = false){


		if (EasyTouch.instance!=null){

			if (isTwoFinger) position = EasyTouch.instance.twoFinger.position;
			if (EasyTouch.instance.touchCameras.Count>0){
				for (int i=0;i<EasyTouch.instance.touchCameras.Count;i++){
					if (EasyTouch.instance.touchCameras[i].camera!=null && EasyTouch.instance.touchCameras[i].camera.enabled){
						if( EasyTouch.instance.GetGameObjectAt( position,EasyTouch.instance.touchCameras[i].camera,EasyTouch.instance.touchCameras[i].guiCamera)){
							return EasyTouch.instance.pickedObject.pickedObj;
						}
						else{
							return null;
						}
					}
				}
			}
		}

		return null;
	}


	public static int GetTouchCount(){
		if (EasyTouch.instance){
			return EasyTouch.instance.input.TouchCount();
		}
		else{
			return 0;
		}
	}

	public static void ResetTouch(int fingerIndex){
		if (EasyTouch.instance)
			EasyTouch.instance.GetFinger(fingerIndex).gesture=GestureType.None;
	}
	

	public static void SetEnabled( bool enable){
		EasyTouch.instance.enable = enable;
		if (enable){
			EasyTouch.instance.ResetTouches();	
		}
	}
	public static bool GetEnabled(){
		if (EasyTouch.instance)
			return EasyTouch.instance.enable;
		else
			return false;
	}

	public static void SetEnableUIDetection( bool enable){
		if (EasyTouch.instance != null){
			EasyTouch.instance.allowUIDetection = enable;
		}
	}
	public static bool GetEnableUIDetection(){
		if (EasyTouch.instance){
			return EasyTouch.instance.allowUIDetection;
		}
		else{
			return false;
		}
	}

	public static void SetUICompatibily(bool value){
		if (EasyTouch.instance != null){
			EasyTouch.instance.enableUIMode = value;
		}
	}
	public static bool GetUIComptability(){
		if (EasyTouch.instance != null){
			return EasyTouch.instance.enableUIMode;
		}
		else{
			return false;
		}
	}
	
	public static void SetAutoUpdateUI(bool value){
		if (EasyTouch.instance)
			EasyTouch.instance.autoUpdatePickedUI = value;
	}
	public static bool GetAutoUpdateUI(){
		if (EasyTouch.instance)
			return EasyTouch.instance.autoUpdatePickedUI;
		else
			return false;
	}
	
	public static void SetNGUICompatibility(bool value){
		if (EasyTouch.instance)
			EasyTouch.instance.enabledNGuiMode = value;
	}
	public static bool GetNGUICompatibility(){
		if (EasyTouch.instance)
			return EasyTouch.instance.enabledNGuiMode;
		else
			return false;
	}
	

	public static void SetEnableAutoSelect( bool value){
		if (EasyTouch.instance)
			EasyTouch.instance.autoSelect = value;
	}
	public static bool GetEnableAutoSelect(){
		if (EasyTouch.instance)
			return EasyTouch.instance.autoSelect;
		else
			return false;
	}

	public static void SetAutoUpdatePickedObject(bool value){
		if (EasyTouch.instance)
			EasyTouch.instance.autoUpdatePickedObject = value;
	}
	public static bool GetAutoUpdatePickedObject(){
		if (EasyTouch.instance)
			return EasyTouch.instance.autoUpdatePickedObject;
		else
			return false;
	}

	public static void Set3DPickableLayer(LayerMask mask){
		if (EasyTouch.instance)
			EasyTouch.instance.pickableLayers3D = mask;	
	}
	public static LayerMask Get3DPickableLayer(){
		if (EasyTouch.instance)
			return EasyTouch.instance.pickableLayers3D;	
		else
			return LayerMask.GetMask("Default");
	}

	public static void AddCamera(Camera cam,bool guiCam=false){
		if (EasyTouch.instance)
			EasyTouch.instance.touchCameras.Add(new ECamera(cam,guiCam));
	}
	public static void RemoveCamera( Camera cam){
		if (EasyTouch.instance){

			int result = EasyTouch.instance.touchCameras.FindIndex(
				delegate( ECamera c){
				return c.camera == cam;
			}
			);

			if (result>-1){
				EasyTouch.instance.touchCameras[result]=null;
				EasyTouch.instance.touchCameras.RemoveAt( result);

			}

		}

	}

	public static Camera GetCamera(int index=0){
		if (EasyTouch.instance){
			if (index< EasyTouch.instance.touchCameras.Count){
				return EasyTouch.instance.touchCameras[index].camera;	
			}
			else{
				return null;	
			}
		}
		else{
			return null;
		}
	}

	public static void SetEnable2DCollider(bool value){
		if (EasyTouch.instance)
			EasyTouch.instance.enable2D = value;
	}
	public static bool GetEnable2DCollider(){
		if (EasyTouch.instance)
			return EasyTouch.instance.enable2D;
		else
			return false;
	}

	public static void Set2DPickableLayer(LayerMask mask){
		if (EasyTouch.instance)
			EasyTouch.instance.pickableLayers2D = mask;	
	}
	public static LayerMask Get2DPickableLayer(){
		if (EasyTouch.instance)
			return EasyTouch.instance.pickableLayers2D;	
		else
			return LayerMask.GetMask("Default");
	}


	public static void SetGesturePriority( GesturePriority value){
		if (EasyTouch.instance)
			EasyTouch.instance.gesturePriority = value;
	}
	public static GesturePriority GetGesturePriority(){
		if (EasyTouch.instance)
			return EasyTouch.instance.gesturePriority;
		else
			return GesturePriority.Tap;
	}

	public static void SetStationaryTolerance(float tolerance){
		if (EasyTouch.instance)
			EasyTouch.instance.StationaryTolerance = tolerance;
	}
	public static float GetStationaryTolerance(){
		if (EasyTouch.instance)
			return EasyTouch.instance.StationaryTolerance;
		else
			return -1;
	}
	
	public static void SetLongTapTime(float time){
		if (EasyTouch.instance)
			EasyTouch.instance.longTapTime = time;
	}
	public static float GetlongTapTime(){
		if (EasyTouch.instance)
			return EasyTouch.instance.longTapTime;
		else
			return -1;
	}

	public static void SetDoubleTapTime(float time){
		if (EasyTouch.instance)
			EasyTouch.instance.doubleTapTime = time;
	}
	public static float GetDoubleTapTime(){
		if (EasyTouch.instance)
			return EasyTouch.instance.doubleTapTime;
		else
			return -1;
	}

	public static void SetDoubleTapMethod(DoubleTapDetection detection){
		if (EasyTouch.instance)
			EasyTouch.instance.doubleTapDetection = detection;
	}
	public static EasyTouch.DoubleTapDetection GetDoubleTapMethod(){
		if (EasyTouch.instance)
			return EasyTouch.instance.doubleTapDetection;
		else
			return EasyTouch.DoubleTapDetection.BySystem;
	}

	public static void SetSwipeTolerance( float tolerance){
		if (EasyTouch.instance)
			EasyTouch.instance.swipeTolerance = tolerance;
	}
	public static float GetSwipeTolerance(){
		if (EasyTouch.instance)
			return EasyTouch.instance.swipeTolerance;
		else
			return -1;
	}
	

	public static void SetEnable2FingersGesture( bool enable){
		if (EasyTouch.instance)
			EasyTouch.instance.enable2FingersGesture = enable;
	}
	public static bool GetEnable2FingersGesture(){
		if (EasyTouch.instance)
			return EasyTouch.instance.enable2FingersGesture;
		else
			return false;
	}

	public static void SetTwoFingerPickMethod( EasyTouch.TwoFingerPickMethod pickMethod){
		if (EasyTouch.instance)
			EasyTouch.instance.twoFingerPickMethod = pickMethod;
	}
	public static EasyTouch.TwoFingerPickMethod GetTwoFingerPickMethod(){
		if (EasyTouch.instance)
			return EasyTouch.instance.twoFingerPickMethod;
		else
			return EasyTouch.TwoFingerPickMethod.Finger;
	}

	public static void SetEnablePinch( bool enable){
		if (EasyTouch.instance)
			EasyTouch.instance.enablePinch = enable;
	}
	public static bool GetEnablePinch(){
		if (EasyTouch.instance)
			return EasyTouch.instance.enablePinch;
		else
			return false;
	}

	public static void SetMinPinchLength(float length){
		if (EasyTouch.instance)
			EasyTouch.instance.minPinchLength=length;
	}
	public static float GetMinPinchLength(){
		if (EasyTouch.instance)
			return EasyTouch.instance.minPinchLength;
		else
			return -1;
	}

	public static void SetEnableTwist( bool enable){
		if (EasyTouch.instance)
			EasyTouch.instance.enableTwist = enable;
	}
	public static bool GetEnableTwist(){
		if (EasyTouch.instance)
			return EasyTouch.instance.enableTwist;
		else
			return false;
	}

	public static void SetMinTwistAngle(float angle){
		if (EasyTouch.instance)
			EasyTouch.instance.minTwistAngle = angle;
	}
	public static float GetMinTwistAngle(){
		if (EasyTouch.instance)
			return EasyTouch.instance.minTwistAngle;
		else 
			return -1;
	}

	public static bool GetSecondeFingerSimulation(){
		
		if (EasyTouch.instance != null){
			return EasyTouch.instance.enableSimulation;
		}
		else{
			return false;
		}
	}
	public static void SetSecondFingerSimulation(bool value){
		if (EasyTouch.instance != null){
			EasyTouch.instance.enableSimulation = value;
		}
	}
	#endregion
	
}
}

挂载不同的脚本

调节控制是否能无视ui交互,移动/旋转方向,单指/多指等参数,实现不同的功能。

例如:

cs 复制代码
/***********************************************
				EasyTouch V
	Copyright © 2014-2015 The Hedgehog Team
    http://www.thehedgehogteam.com/Forum/
		
	  The.Hedgehog.Team@gmail.com
		
**********************************************/
using UnityEngine;
using System.Collections;
using UnityEngine.Events;
using UnityEngine.EventSystems;

namespace HedgehogTeam.EasyTouch{
[AddComponentMenu("EasyTouch/Quick Swipe")]
public class QuickSwipe : QuickBase {

	#region Events
	[System.Serializable] public class OnSwipeAction : UnityEvent<Gesture>{}

	[SerializeField] 
	public OnSwipeAction onSwipeAction;
	#endregion

	#region enumeration
	public enum ActionTriggering {InProgress,End}
	public enum SwipeDirection {Vertical, Horizontal, DiagonalRight,DiagonalLeft,Up,UpRight, Right,DownRight,Down,DownLeft, Left,UpLeft,All};
	#endregion

	#region Members
	public bool allowSwipeStartOverMe = true;
	public ActionTriggering actionTriggering;
	public SwipeDirection swipeDirection = SwipeDirection.All;
	private float axisActionValue = 0;
	public bool enableSimpleAction = false;
	#endregion

	#region MonoBehaviour callback
	public QuickSwipe(){
		quickActionName = "QuickSwipe" + System.Guid.NewGuid().ToString().Substring(0,7);
	}

	public override void OnEnable(){
		base.OnEnable();
		EasyTouch.On_Drag += On_Drag;
		EasyTouch.On_Swipe += On_Swipe;
		EasyTouch.On_DragEnd += On_DragEnd;
		EasyTouch.On_SwipeEnd += On_SwipeEnd;
	}

	public override void OnDisable(){
		base.OnDisable();
		UnsubscribeEvent();
	}
	
	void OnDestroy(){
		UnsubscribeEvent();
	}
	
	void UnsubscribeEvent(){
		EasyTouch.On_Drag -= On_Drag;
		EasyTouch.On_Swipe -= On_Swipe;
		EasyTouch.On_DragEnd -= On_DragEnd;
		EasyTouch.On_SwipeEnd -= On_SwipeEnd;
	}
	#endregion

	#region EasyTouch Event
	void On_Swipe (Gesture gesture){

		if (gesture.touchCount ==1 && ((gesture.pickedObject != gameObject && !allowSwipeStartOverMe) || allowSwipeStartOverMe)){
			fingerIndex = gesture.fingerIndex;
			if (actionTriggering == ActionTriggering.InProgress){
				if (isRightDirection(gesture)){
					onSwipeAction.Invoke( gesture);
					if (enableSimpleAction){
						DoAction(gesture);
					}
				}
			}
		}
	}

	void On_SwipeEnd (Gesture gesture){
			if (actionTriggering == ActionTriggering.End && isRightDirection(gesture) ){
			onSwipeAction.Invoke( gesture);
			if (enableSimpleAction){
				DoAction(gesture);
			}
		}

		if (fingerIndex ==  gesture.fingerIndex){
			fingerIndex =-1;
		}
	}

	void On_DragEnd (Gesture gesture){
		if (gesture.pickedObject == gameObject && allowSwipeStartOverMe){
			On_SwipeEnd( gesture);
		}
	}
	
	void On_Drag (Gesture gesture){
		if (gesture.pickedObject == gameObject && allowSwipeStartOverMe){
			On_Swipe( gesture);
		}
	}
	#endregion

	#region Private methods
	bool isRightDirection(Gesture gesture){
		float coef = -1;
		if ( inverseAxisValue){
			coef = 1;
		}

		axisActionValue = 0;
		switch (swipeDirection){
		case SwipeDirection.All:
			axisActionValue = gesture.deltaPosition.magnitude*coef;
			return true;
		case SwipeDirection.Horizontal:
			if (gesture.swipe == EasyTouch.SwipeDirection.Left || gesture.swipe == EasyTouch.SwipeDirection.Right){
				axisActionValue = gesture.deltaPosition.x *coef;
				return true;
			}
			break;
		case SwipeDirection.Vertical:
			if (gesture.swipe == EasyTouch.SwipeDirection.Up || gesture.swipe == EasyTouch.SwipeDirection.Down){
				axisActionValue = gesture.deltaPosition.y*coef;
				return true;
			}
			break;
		case SwipeDirection.DiagonalLeft:
			if (gesture.swipe == EasyTouch.SwipeDirection.UpLeft || gesture.swipe == EasyTouch.SwipeDirection.DownRight){
				axisActionValue = gesture.deltaPosition.magnitude*coef;
				return true;
			}
			break;
		case SwipeDirection.DiagonalRight:
			if (gesture.swipe == EasyTouch.SwipeDirection.UpRight || gesture.swipe == EasyTouch.SwipeDirection.DownLeft){
				axisActionValue = gesture.deltaPosition.magnitude*coef;
				return true;
			}

			break;
		case SwipeDirection.Left:
			if (gesture.swipe == EasyTouch.SwipeDirection.Left){
				axisActionValue = gesture.deltaPosition.x*coef;
				return true;
			}
			break;
		case SwipeDirection.Right:
			if (gesture.swipe == EasyTouch.SwipeDirection.Right){
				axisActionValue = gesture.deltaPosition.x*coef;
				return true;
			}
			break;
		case SwipeDirection.DownLeft:
			if (gesture.swipe == EasyTouch.SwipeDirection.DownLeft){
				axisActionValue = gesture.deltaPosition.magnitude*coef;
				return true;
			}
			break;
		case SwipeDirection.DownRight:
			if (gesture.swipe == EasyTouch.SwipeDirection.DownRight){

				axisActionValue = gesture.deltaPosition.magnitude*coef;
				return true;
			}
			break;
		case SwipeDirection.UpLeft:
			if (gesture.swipe == EasyTouch.SwipeDirection.UpLeft){

				axisActionValue = gesture.deltaPosition.magnitude*coef;
				return true;
			}
			break;
		case SwipeDirection.UpRight:
			if (gesture.swipe == EasyTouch.SwipeDirection.UpRight){
				axisActionValue = gesture.deltaPosition.magnitude*coef;
				return true;
			}
			break;
		case SwipeDirection.Up:
			if (gesture.swipe == EasyTouch.SwipeDirection.Up){
				axisActionValue = gesture.deltaPosition.y*coef;
				return true;
			}
			break;
		case SwipeDirection.Down:
			if (gesture.swipe == EasyTouch.SwipeDirection.Down){
				axisActionValue = gesture.deltaPosition.y*coef;
				return true;
			}
			break;
		}

		axisActionValue = 0;
		return false;
	}

	void DoAction(Gesture gesture){

		switch (directAction){
		case DirectAction.Rotate:
		case DirectAction.RotateLocal:
			axisActionValue *= sensibility;
			break;
		case DirectAction.Translate:
		case DirectAction.TranslateLocal:
		case DirectAction.Scale:
			axisActionValue /= Screen.dpi;
			axisActionValue *= sensibility;
			break;
		}

		DoDirectAction( axisActionValue);
	
	}
	#endregion
}
}
相关推荐
阿巴斯甜16 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker17 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952718 小时前
Andorid Google 登录接入文档
android
黄林晴19 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android