手势插件引入暂存
This commit is contained in:
1 parent
6d1bc8e2c8
commit
fd8f419f09
70 files changed
+5062
-126
No files matched your search
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34c845883c036418db85a57ce0664fe3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,44 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
|
||||
[CustomEditor( typeof( TouchKit ) )]
|
||||
public class TouchKitEditor : Editor
|
||||
{
|
||||
private bool showDebug = true;
|
||||
private string status = "Touch Debugging";
|
||||
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
|
||||
showDebug = EditorGUILayout.Foldout( showDebug, status );
|
||||
if( showDebug )
|
||||
{
|
||||
TouchKit touchKit = (TouchKit)target;
|
||||
touchKit.drawDebugBoundaryFrames = EditorGUILayout.Toggle( "Draw boundary frames", touchKit.drawDebugBoundaryFrames );
|
||||
touchKit.drawTouches = EditorGUILayout.Toggle( "Draw touches", touchKit.drawTouches );
|
||||
touchKit.simulateTouches = EditorGUILayout.Toggle( "Simulate touches", touchKit.simulateTouches );
|
||||
|
||||
GUI.enabled = touchKit.simulateTouches;
|
||||
if( GUI.enabled || true )
|
||||
{
|
||||
var helpText = "Touches can be simulated in the editor or on the desktop with mouse clicks.";
|
||||
if( touchKit.simulateMultitouch )
|
||||
helpText += "\nTo simulate a two-finger gesture, press and hold the left alt key and move your mouse around. Shift the touches by additionally holding down left shift.";
|
||||
|
||||
EditorGUILayout.HelpBox( helpText, MessageType.Info, true );
|
||||
}
|
||||
|
||||
touchKit.simulateMultitouch = EditorGUILayout.Toggle( "Simulate multitouch", touchKit.simulateMultitouch );
|
||||
}
|
||||
|
||||
if( GUI.changed )
|
||||
EditorUtility.SetDirty( target );
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e5c8365f45374e259a591b8b76af04a
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c88906649098c4a669eace1b1c825a02
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,94 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// replacement for the Unity Rect class that is TouchKit and resolution-aware. Creating one will automatically scale all values
|
||||
/// if TouchKit autoScaleRectsAndDistances is true based on your designTimeResolution.
|
||||
///
|
||||
/// Note the TKRects use the bottom-left as the origin.
|
||||
/// </summary>
|
||||
public struct TKRect
|
||||
{
|
||||
public float x;
|
||||
public float y;
|
||||
public float width;
|
||||
public float height;
|
||||
|
||||
public float xMin { get { return x; } }
|
||||
public float xMax { get { return x + width; } }
|
||||
public float yMin { get { return y; } }
|
||||
public float yMax { get { return y + height; } }
|
||||
public Vector2 center { get { return new Vector2( x + ( width / 2f ), y + ( height / 2f ) ); } }
|
||||
|
||||
|
||||
public TKRect( float x, float y, float width, float height )
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
updateRectWithRuntimeScaleModifier();
|
||||
}
|
||||
|
||||
|
||||
public TKRect( float width, float height, Vector2 center )
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.x = center.x - width / 2f;
|
||||
this.y = center.y - height / 2f;
|
||||
|
||||
updateRectWithRuntimeScaleModifier();
|
||||
}
|
||||
|
||||
|
||||
private void updateRectWithRuntimeScaleModifier()
|
||||
{
|
||||
var multiplier = TouchKit.instance.runtimeScaleModifier;
|
||||
|
||||
x *= multiplier.x;
|
||||
y *= multiplier.y;
|
||||
width = width * multiplier.x;
|
||||
height = height * multiplier.y;
|
||||
}
|
||||
|
||||
|
||||
public TKRect copyWithExpansion( float allSidesExpansion )
|
||||
{
|
||||
return copyWithExpansion( allSidesExpansion, allSidesExpansion );
|
||||
}
|
||||
|
||||
|
||||
public TKRect copyWithExpansion( float xExpansion, float yExpansion )
|
||||
{
|
||||
xExpansion *= TouchKit.instance.runtimeScaleModifier.x;
|
||||
yExpansion *= TouchKit.instance.runtimeScaleModifier.y;
|
||||
|
||||
var rect = new TKRect();
|
||||
rect.x = this.x - xExpansion;
|
||||
rect.y = this.y - yExpansion;
|
||||
rect.width = this.width + ( xExpansion * 2f );
|
||||
rect.height = this.height + ( yExpansion * 2f );
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
|
||||
public bool contains( Vector2 point )
|
||||
{
|
||||
if( x <= point.x && y <= point.y && xMax >= point.x && yMax >= point.y )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "TKRect: x: {0}, xMax: {1}, y: {2}, yMax: {3}, width: {4}, height: {5}, center: {6}",
|
||||
x, xMax, y, yMax, width, height, center );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f68dd8df2e674a94939789235fb9b28
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,130 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
|
||||
public class TKTouch
|
||||
{
|
||||
public readonly int fingerId;
|
||||
public Vector2 position;
|
||||
public Vector2 startPosition;
|
||||
public Vector2 deltaPosition;
|
||||
public float deltaTime;
|
||||
public int tapCount;
|
||||
public TouchPhase phase = TouchPhase.Ended;
|
||||
|
||||
public Vector2 previousPosition
|
||||
{
|
||||
get { return position - deltaPosition; }
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_WEBPLAYER || UNITY_WEBGL
|
||||
// used to track mouse movement and fake touches
|
||||
private Vector2? _lastPosition;
|
||||
private double _lastClickTime;
|
||||
private double _multipleClickInterval = 0.2;
|
||||
#endif
|
||||
|
||||
|
||||
public TKTouch( int fingerId )
|
||||
{
|
||||
// lock this TKTouch to the fingerId
|
||||
this.fingerId = fingerId;
|
||||
}
|
||||
|
||||
public TKTouch populateWithTouch( Touch touch )
|
||||
{
|
||||
position = touch.position;
|
||||
deltaPosition = touch.deltaPosition;
|
||||
deltaTime = touch.deltaTime;
|
||||
tapCount = touch.tapCount;
|
||||
|
||||
if (touch.phase== TouchPhase.Began)
|
||||
{
|
||||
startPosition = position;
|
||||
}
|
||||
|
||||
// canceled and ended are the same to us
|
||||
if( touch.phase == TouchPhase.Canceled )
|
||||
phase = TouchPhase.Ended;
|
||||
else
|
||||
phase = touch.phase;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_WEBPLAYER || UNITY_WEBGL
|
||||
public TKTouch populateWithPosition( Vector3 currentPosition, TouchPhase touchPhase )
|
||||
{
|
||||
var currentPosition2d = new Vector2( currentPosition.x, currentPosition.y );
|
||||
|
||||
// if we have a lastMousePosition use it to get a delta
|
||||
if( _lastPosition.HasValue )
|
||||
deltaPosition = currentPosition2d - _lastPosition.Value;
|
||||
else
|
||||
deltaPosition = new Vector2( 0, 0 );
|
||||
|
||||
switch( touchPhase )
|
||||
{
|
||||
case TouchPhase.Began:
|
||||
phase = TouchPhase.Began;
|
||||
|
||||
// check for multiple clicks
|
||||
if( Time.time < _lastClickTime + _multipleClickInterval )
|
||||
tapCount++;
|
||||
else
|
||||
tapCount = 1;
|
||||
_lastPosition = currentPosition2d;
|
||||
startPosition = currentPosition2d;
|
||||
_lastClickTime = Time.time;
|
||||
break;
|
||||
case TouchPhase.Stationary:
|
||||
case TouchPhase.Moved:
|
||||
if( deltaPosition.sqrMagnitude == 0 )
|
||||
phase = TouchPhase.Stationary;
|
||||
else
|
||||
phase = TouchPhase.Moved;
|
||||
|
||||
_lastPosition = currentPosition2d;
|
||||
break;
|
||||
case TouchPhase.Ended:
|
||||
phase = TouchPhase.Ended;
|
||||
_lastPosition = null;
|
||||
break;
|
||||
}
|
||||
|
||||
position = currentPosition2d;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TKTouch populateFromMouse()
|
||||
{
|
||||
// do we have some input to work with?
|
||||
if( Input.GetMouseButtonUp( 0 ) || Input.GetMouseButton( 0 ) )
|
||||
{
|
||||
var phase = TouchPhase.Moved;
|
||||
|
||||
// guard against down and up being called in the same frame
|
||||
if( Input.GetMouseButtonDown( 0 ) && Input.GetMouseButtonUp( 0 ) )
|
||||
phase = TouchPhase.Canceled;
|
||||
else if( Input.GetMouseButtonUp( 0 ) )
|
||||
phase = TouchPhase.Ended;
|
||||
else if( Input.GetMouseButtonDown( 0 ) )
|
||||
phase = TouchPhase.Began;
|
||||
|
||||
var currentMousePosition = new Vector2( Input.mousePosition.x, Input.mousePosition.y );
|
||||
this.populateWithPosition( currentMousePosition, phase );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "[TKTouch] fingerId: {0}, phase: {1}, position: {2}", fingerId, phase, position );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2defb83e5e57b48bfb27864a8ab5e134
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,171 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// this only exists in the editor to assist with testing and simulating touches and keeping the main class clean
|
||||
/// </summary>
|
||||
public partial class TouchKit
|
||||
{
|
||||
private Vector3? _simulatedMultitouchStartPosition;
|
||||
private Vector3 _simulatedMousePosition;
|
||||
private bool _hasActiveSimulatedTouch;
|
||||
private bool _hasActiveSimulatedMultitouch;
|
||||
private bool _hasUnityRemoteActive;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// returns true if mouse input should be processed as touch input. it will be true when the Unity Remote is not active.
|
||||
/// </summary>
|
||||
private bool shouldProcessMouseInput()
|
||||
{
|
||||
if( !simulateTouches )
|
||||
return false;
|
||||
|
||||
// check to see if the Unity Remote is active
|
||||
if( Input.touchCount > 0 )
|
||||
{
|
||||
Debug.LogWarning( "disabling touch simulation because we detected a Unity Remote connected" );
|
||||
simulateTouches = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if enabled and alt is being held down we are simulating pinching
|
||||
if( simulateMultitouch && ( _hasActiveSimulatedMultitouch || Input.GetKey( KeyCode.LeftAlt ) || Input.GetKeyUp( KeyCode.LeftAlt ) ) )
|
||||
{
|
||||
if( Input.GetKeyDown( KeyCode.LeftAlt ) )
|
||||
{
|
||||
_simulatedMultitouchStartPosition = Input.mousePosition;
|
||||
}
|
||||
else if( Input.GetKey( KeyCode.LeftShift ) )
|
||||
{
|
||||
// calculate the last mouse position from the simulated position and shift the start position acordingly
|
||||
var lastMousePosition = _simulatedMultitouchStartPosition.Value + ( _simulatedMultitouchStartPosition.Value - _simulatedMousePosition );
|
||||
var diff = Input.mousePosition - lastMousePosition;
|
||||
_simulatedMultitouchStartPosition += diff;
|
||||
}
|
||||
|
||||
if( Input.GetKey( KeyCode.LeftAlt ) || Input.GetKeyUp( KeyCode.LeftAlt ) )
|
||||
{
|
||||
var diff = new Vector3( Input.mousePosition.x - _simulatedMultitouchStartPosition.Value.x, Input.mousePosition.y - _simulatedMultitouchStartPosition.Value.y );
|
||||
_simulatedMousePosition = _simulatedMultitouchStartPosition.Value - diff;
|
||||
}
|
||||
|
||||
TouchPhase? touchPhase = null;
|
||||
if( Input.GetKey( KeyCode.LeftAlt ) && Input.GetMouseButton( 0 ) )
|
||||
{
|
||||
// if we haven't started yet, add a touch began, else move
|
||||
if( !_hasActiveSimulatedMultitouch )
|
||||
{
|
||||
_hasActiveSimulatedMultitouch = true;
|
||||
touchPhase = TouchPhase.Began;
|
||||
}
|
||||
else
|
||||
{
|
||||
touchPhase = TouchPhase.Moved;
|
||||
}
|
||||
}
|
||||
|
||||
if( ( Input.GetKeyUp( KeyCode.LeftAlt ) || Input.GetMouseButtonUp(0)) && _hasActiveSimulatedMultitouch )
|
||||
{
|
||||
touchPhase = TouchPhase.Ended;
|
||||
_hasActiveSimulatedMultitouch = false;
|
||||
}
|
||||
|
||||
|
||||
if( touchPhase.HasValue )
|
||||
{
|
||||
// we need to set up a second touch
|
||||
_liveTouches.Add( _touchCache[1].populateWithPosition( _simulatedMousePosition, touchPhase.Value ) );
|
||||
}
|
||||
|
||||
if( Input.GetKeyUp( KeyCode.LeftAlt ) )
|
||||
{
|
||||
_simulatedMultitouchStartPosition = null;
|
||||
}
|
||||
}
|
||||
|
||||
_hasActiveSimulatedTouch = Input.GetMouseButton( 0 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// this is for debugging only while in the editor
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if( _instance == null )
|
||||
return;
|
||||
|
||||
if( drawTouches )
|
||||
{
|
||||
// draw a green point for all active touches, including the touches from Unity remote
|
||||
foreach( TKTouch touch in _touchCache )
|
||||
{
|
||||
if( touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary )
|
||||
{
|
||||
var touchPos = Camera.main.ScreenToWorldPoint( new Vector3( touch.position.x, touch.position.y, Camera.main.farClipPlane ) );
|
||||
Gizmos.DrawIcon( touchPos, "greenPoint.png", false );
|
||||
}
|
||||
}
|
||||
|
||||
if( _simulatedMultitouchStartPosition.HasValue && !_hasActiveSimulatedTouch )
|
||||
{
|
||||
var mousePos = Camera.main.ScreenToWorldPoint( new Vector3( Input.mousePosition.x, Input.mousePosition.y, Camera.main.farClipPlane ) );
|
||||
Gizmos.DrawIcon( mousePos, "redPoint.png", false );
|
||||
|
||||
var simulatedPos = Camera.main.ScreenToWorldPoint( new Vector3( _simulatedMousePosition.x, _simulatedMousePosition.y, Camera.main.farClipPlane ) );
|
||||
Gizmos.DrawIcon( simulatedPos, "redPoint.png", false );
|
||||
}
|
||||
}
|
||||
|
||||
if( drawDebugBoundaryFrames )
|
||||
{
|
||||
var colors = new Color[]
|
||||
{
|
||||
Color.red,
|
||||
Color.cyan,
|
||||
Color.red,
|
||||
Color.magenta,
|
||||
Color.yellow
|
||||
};
|
||||
int i = 0;
|
||||
|
||||
foreach( var r in _gestureRecognizers )
|
||||
{
|
||||
if( r.boundaryFrame.HasValue )
|
||||
{
|
||||
debugDrawRect( r.boundaryFrame.Value, colors [i] );
|
||||
if( ++i >= colors.Length )
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void debugDrawRect( TKRect rect, Color color )
|
||||
{
|
||||
var bl = new Vector3( rect.xMin, rect.yMin, 0 );
|
||||
var br = new Vector3( rect.xMax, rect.yMin, 0 );
|
||||
var tl = new Vector3( rect.xMin, rect.yMax, 0 );
|
||||
var tr = new Vector3( rect.xMax, rect.yMax, 0 );
|
||||
|
||||
bl = Camera.main.ScreenToWorldPoint( new Vector3( bl.x, bl.y, Camera.main.farClipPlane ) );
|
||||
br = Camera.main.ScreenToWorldPoint( new Vector3( br.x, br.y, Camera.main.farClipPlane ) );
|
||||
tl = Camera.main.ScreenToWorldPoint( new Vector3( tl.x, tl.y, Camera.main.farClipPlane ) );
|
||||
tr = Camera.main.ScreenToWorldPoint( new Vector3( tr.x, tr.y, Camera.main.farClipPlane ) );
|
||||
|
||||
// draw four sides
|
||||
Debug.DrawLine( bl, br, color );
|
||||
Debug.DrawLine( br, tr, color );
|
||||
Debug.DrawLine( tr, tl, color );
|
||||
Debug.DrawLine( tl, bl, color );
|
||||
|
||||
// make an "x" at the midpoint
|
||||
Debug.DrawLine( tl, br, color );
|
||||
Debug.DrawLine( bl, tr, color );
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1f6cfc9c43424c2eaafa3f02ee2fb90
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c43f7b533c50c424fbbfc3ea8d40548e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,288 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
public enum TKGestureRecognizerState
|
||||
{
|
||||
Possible, // we havent started yet and we are still listening
|
||||
Began, // we have started and latched at least one finger
|
||||
FailedOrEnded, // no go. failed to recognize or we are done recognizing
|
||||
RecognizedAndStillRecognizing, // this will fire the state changed event and allow a gesture to continue to recognize. useful for continuous gestures
|
||||
Recognized // successfully recognized and we are not a continuous recognizer
|
||||
}
|
||||
|
||||
|
||||
public abstract class TKAbstractGestureRecognizer : IComparable<TKAbstractGestureRecognizer>
|
||||
{
|
||||
public bool enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// frame that the touch must be within to be recognized. null means full screen. note that Unity's origin is the bottom left
|
||||
/// </summary>
|
||||
public TKRect? boundaryFrame = null;
|
||||
|
||||
/// <summary>
|
||||
/// zIndex of touch input. 0 by default. if a zIndex of greater than 0 uses a touch in touchesBegan it will not be passed to any other recognizers.
|
||||
/// useful if you have some full screen recognizers and you want to overlay a button/control
|
||||
/// </summary>
|
||||
public uint zIndex = 0;
|
||||
|
||||
private TKGestureRecognizerState _state = TKGestureRecognizerState.Possible;
|
||||
public TKGestureRecognizerState state
|
||||
{
|
||||
get { return _state; }
|
||||
set
|
||||
{
|
||||
_state = value;
|
||||
|
||||
if( _state == TKGestureRecognizerState.Recognized || _state == TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
fireRecognizedEvent();
|
||||
|
||||
if( _state == TKGestureRecognizerState.Recognized || _state == TKGestureRecognizerState.FailedOrEnded )
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// when true, touchesMoved will be called for ALL touches. By default, only the touches
|
||||
/// a recognizer is tracking (from touchesBegan) will be sent.
|
||||
/// </summary>
|
||||
protected bool alwaysSendTouchesMoved = false;
|
||||
|
||||
/// <summary>
|
||||
/// stores all the touches we are currently tracking
|
||||
/// </summary>
|
||||
protected List<TKTouch> _trackingTouches = new List<TKTouch>();
|
||||
|
||||
/// <summary>
|
||||
/// The subset of touches being tracked that is applicable to the current recognizer. This is kept around to avoid allocations at runtime.
|
||||
/// </summary>
|
||||
private List<TKTouch> _subsetOfTouchesBeingTrackedApplicableToCurrentRecognizer = new List<TKTouch>();
|
||||
|
||||
/// <summary>
|
||||
/// stores whether we sent any of the phases to the recognizer. This is to avoid sending a phase twice in one frame.
|
||||
/// </summary>
|
||||
private bool _sentTouchesBegan;
|
||||
private bool _sentTouchesMoved;
|
||||
private bool _sentTouchesEnded;
|
||||
|
||||
/// <summary>
|
||||
/// checks to see if the touch is currently being tracked by the recognizer
|
||||
/// </summary>
|
||||
protected bool isTrackingTouch( TKTouch t )
|
||||
{
|
||||
return _trackingTouches.Contains( t );
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// checks to see if any of the touches are currently being tracked by the recognizer
|
||||
/// </summary>
|
||||
protected bool isTrackingAnyTouch( List<TKTouch> touches )
|
||||
{
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if( _trackingTouches.Contains( touches[i] ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// populates the _subsetOfTouchesBeingTrackedApplicableToCurrentRecognizer with only the touches currently being tracked by the recognizer.
|
||||
/// returns true if there are any touches being tracked
|
||||
/// </summary>
|
||||
private bool populateSubsetOfTouchesBeingTracked( List<TKTouch> touches )
|
||||
{
|
||||
_subsetOfTouchesBeingTrackedApplicableToCurrentRecognizer.Clear();
|
||||
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if( alwaysSendTouchesMoved || isTrackingTouch( touches[i] ) )
|
||||
_subsetOfTouchesBeingTrackedApplicableToCurrentRecognizer.Add( touches[i] );
|
||||
}
|
||||
|
||||
return _subsetOfTouchesBeingTrackedApplicableToCurrentRecognizer.Count > 0;
|
||||
}
|
||||
|
||||
|
||||
private bool shouldAttemptToRecognize
|
||||
{
|
||||
get
|
||||
{
|
||||
return ( enabled && state != TKGestureRecognizerState.FailedOrEnded && state != TKGestureRecognizerState.Recognized );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Public API
|
||||
|
||||
internal void recognizeTouches( List<TKTouch> touches )
|
||||
{
|
||||
if( !shouldAttemptToRecognize )
|
||||
return;
|
||||
|
||||
// reset our state to avoid sending any phase more than once
|
||||
_sentTouchesBegan = _sentTouchesMoved = _sentTouchesEnded = false;
|
||||
|
||||
// we loop backwards because the Began phase could end up removing a touch
|
||||
for( var i = touches.Count - 1; i >= 0; i-- )
|
||||
{
|
||||
var touch = touches[i];
|
||||
switch( touch.phase )
|
||||
{
|
||||
case TouchPhase.Began:
|
||||
{
|
||||
// only send touches began once and ensure that the touch is in the boundaryFrame if applicable
|
||||
if( !_sentTouchesBegan && isTouchWithinBoundaryFrame( touches[i] ) )
|
||||
{
|
||||
// if touchesBegan returns true and we have a zIndex greater than 0 we remove the touches with a phase of Began
|
||||
if( touchesBegan( touches ) && zIndex > 0 )
|
||||
{
|
||||
// if we remove more than one touch we have to be careful with our loop and make sure to decrement i appropriately
|
||||
var removedTouches = 0;
|
||||
for( var j = touches.Count - 1; j >= 0; j-- )
|
||||
{
|
||||
if( touches[j].phase == TouchPhase.Began )
|
||||
{
|
||||
touches.RemoveAt( j );
|
||||
removedTouches++;
|
||||
}
|
||||
}
|
||||
|
||||
// if we removed more than 1 touch decrement i for each additional touch removed
|
||||
if( removedTouches > 0 )
|
||||
i -= ( removedTouches - 1 );
|
||||
}
|
||||
_sentTouchesBegan = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TouchPhase.Moved:
|
||||
{
|
||||
// limit touches sent to those that are being tracked
|
||||
if( !_sentTouchesMoved && populateSubsetOfTouchesBeingTracked( touches ) && _subsetOfTouchesBeingTrackedApplicableToCurrentRecognizer.Contains( touch ) )
|
||||
{
|
||||
touchesMoved( _subsetOfTouchesBeingTrackedApplicableToCurrentRecognizer );
|
||||
_sentTouchesMoved = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TouchPhase.Ended:
|
||||
case TouchPhase.Canceled:
|
||||
{
|
||||
// limit touches sent to those that are being tracked
|
||||
if( !_sentTouchesEnded && populateSubsetOfTouchesBeingTracked( touches ) && _subsetOfTouchesBeingTrackedApplicableToCurrentRecognizer.Contains( touch ) )
|
||||
{
|
||||
touchesEnded( _subsetOfTouchesBeingTrackedApplicableToCurrentRecognizer );
|
||||
_sentTouchesEnded = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal void reset()
|
||||
{
|
||||
_state = TKGestureRecognizerState.Possible;
|
||||
_trackingTouches.Clear();
|
||||
}
|
||||
|
||||
|
||||
internal bool isTouchWithinBoundaryFrame( TKTouch touch )
|
||||
{
|
||||
return !boundaryFrame.HasValue || boundaryFrame.Value.contains( touch.position );
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// returns the location of the touches. If there are multiple touches this will return the centroid of the location.
|
||||
/// </summary>
|
||||
public Vector2 touchLocation()
|
||||
{
|
||||
var x = 0f;
|
||||
var y = 0f;
|
||||
var k = 0f;
|
||||
|
||||
for( var i = 0; i < _trackingTouches.Count; i++ )
|
||||
{
|
||||
x += _trackingTouches[i].position.x;
|
||||
y += _trackingTouches[i].position.y;
|
||||
k++;
|
||||
}
|
||||
|
||||
if( k > 0 )
|
||||
return new Vector2( x / k, y / k );
|
||||
else
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns the start location of the touches. If there are multiple touches this will return the centroid of the location.
|
||||
/// </summary>
|
||||
public Vector2 startTouchLocation()
|
||||
{
|
||||
var x = 0f;
|
||||
var y = 0f;
|
||||
var k = 0f;
|
||||
|
||||
for( var i = 0; i < _trackingTouches.Count; i++ )
|
||||
{
|
||||
x += _trackingTouches[i].startPosition.x;
|
||||
y += _trackingTouches[i].startPosition.y;
|
||||
k++;
|
||||
}
|
||||
|
||||
if( k > 0 )
|
||||
return new Vector2( x / k, y / k );
|
||||
else
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// return true if a touch was used, false if none were. this is used by any recognizers that should swallow touches if on a higher than 0 zIndex
|
||||
/// </summary>
|
||||
internal virtual bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal virtual void touchesMoved( List<TKTouch> touches )
|
||||
{}
|
||||
|
||||
|
||||
internal virtual void touchesEnded( List<TKTouch> touches )
|
||||
{}
|
||||
|
||||
|
||||
internal abstract void fireRecognizedEvent();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region IComparable and ToString implementation
|
||||
|
||||
public int CompareTo( TKAbstractGestureRecognizer other )
|
||||
{
|
||||
return zIndex.CompareTo( other.zIndex );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "[{0}] state: {1}, location: {2}, zIndex: {3}", this.GetType(), state, touchLocation(), zIndex );
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5f52cf2673474a919f25105431aaca6
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,174 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class TKAngleSwipeRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
|
||||
public event Action<TKAngleSwipeRecognizer> gestureRecognizedEvent;
|
||||
|
||||
private struct AngleListener
|
||||
{
|
||||
public float Varience;
|
||||
public Vector2 Direction;
|
||||
public Action<TKAngleSwipeRecognizer> Action;
|
||||
|
||||
public AngleListener (Vector2 direction, float varience, Action<TKAngleSwipeRecognizer> action) : this ()
|
||||
{
|
||||
Varience = varience;
|
||||
Direction = direction;
|
||||
Action = action;
|
||||
}
|
||||
}
|
||||
|
||||
private List<AngleListener> _angleRecognizedEvents = new List<AngleListener> ();
|
||||
|
||||
public float timeToSwipe = 0.5f;
|
||||
|
||||
public float swipeVelocity { get; private set; }
|
||||
|
||||
public float swipeAngle { get; private set; }
|
||||
|
||||
public Vector2 swipeVelVector { get; private set; }
|
||||
|
||||
public float minimumDistance = 2f;
|
||||
|
||||
// swipe state info
|
||||
private Vector2 _startPoint;
|
||||
private Vector2 _endPoint;
|
||||
private float _startTime;
|
||||
|
||||
public Vector2 startPoint {
|
||||
get { return this._startPoint; }
|
||||
}
|
||||
|
||||
public Vector2 endPoint {
|
||||
get { return this._endPoint; }
|
||||
}
|
||||
|
||||
public TKAngleSwipeRecognizer () : this (2f)
|
||||
{
|
||||
}
|
||||
|
||||
public TKAngleSwipeRecognizer (float minimumDistanceCm)
|
||||
{
|
||||
minimumDistance = minimumDistanceCm;
|
||||
}
|
||||
|
||||
public void addAngleRecogizedEvents (Action<TKAngleSwipeRecognizer> action, Vector2 direction, float angleVarience)
|
||||
{
|
||||
_angleRecognizedEvents.Add (new AngleListener (direction, angleVarience, action));
|
||||
}
|
||||
|
||||
public void removeAngleRecognizedEvents (Action<TKAngleSwipeRecognizer> action)
|
||||
{
|
||||
_angleRecognizedEvents.RemoveAll (
|
||||
(AngleListener listener) => {
|
||||
return listener.Action == action;
|
||||
});
|
||||
}
|
||||
|
||||
public void removeAllAngleRecongnizedEvents ()
|
||||
{
|
||||
_angleRecognizedEvents.Clear ();
|
||||
}
|
||||
|
||||
public void fireAngleRecognizedEvents ()
|
||||
{
|
||||
for (int i = 0, length = _angleRecognizedEvents.Count; i < length; i++)
|
||||
{
|
||||
var angleEvent = _angleRecognizedEvents [i];
|
||||
if (angleEvent.Varience > Vector2.Angle (angleEvent.Direction, swipeVelVector))
|
||||
{
|
||||
angleEvent.Action (this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool checkForSwipeCompletion (TKTouch touch)
|
||||
{
|
||||
// if we have a time stipulation and we exceeded it stop listening for swipes
|
||||
if (timeToSwipe > 0.0f && (Time.time - _startTime) > timeToSwipe)
|
||||
{
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Grab the total distance moved in both directions
|
||||
var xDeltaAbsCm = Mathf.Abs (_startPoint.x - touch.position.x) / TouchKit.instance.ScreenPixelsPerCm;
|
||||
var yDeltaAbsCm = Mathf.Abs (_startPoint.y - touch.position.y) / TouchKit.instance.ScreenPixelsPerCm;
|
||||
|
||||
_endPoint = touch.position;
|
||||
|
||||
// Get the velocity
|
||||
swipeVelocity = Mathf.Sqrt (xDeltaAbsCm * xDeltaAbsCm + yDeltaAbsCm * yDeltaAbsCm);
|
||||
|
||||
// Calculate the movement vector
|
||||
var displacement = endPoint - startPoint;
|
||||
// Calculate the angle with respect to the x axis
|
||||
swipeAngle = Mathf.Rad2Deg * Mathf.Atan2 (displacement.y, displacement.x);
|
||||
// Get it between 0 to 360
|
||||
if (swipeAngle < 0)
|
||||
{
|
||||
swipeAngle += 360;
|
||||
}
|
||||
|
||||
swipeVelVector = _endPoint - _startPoint;
|
||||
|
||||
// If we have enough distance, then return true
|
||||
if (swipeVelocity > minimumDistance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void fireRecognizedEvent ()
|
||||
{
|
||||
if (gestureRecognizedEvent != null)
|
||||
{
|
||||
gestureRecognizedEvent (this);
|
||||
}
|
||||
fireAngleRecognizedEvents ();
|
||||
}
|
||||
|
||||
|
||||
internal override bool touchesBegan (List<TKTouch> touches)
|
||||
{
|
||||
if (state == TKGestureRecognizerState.Possible)
|
||||
{
|
||||
_startPoint = touches [0].position;
|
||||
_startTime = Time.time;
|
||||
_trackingTouches.Add (touches [0]);
|
||||
|
||||
state = TKGestureRecognizerState.Began;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved (List<TKTouch> touches)
|
||||
{
|
||||
if (state == TKGestureRecognizerState.Began)
|
||||
{
|
||||
if (checkForSwipeCompletion (touches [0]))
|
||||
{
|
||||
state = TKGestureRecognizerState.Recognized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded (List<TKTouch> touches)
|
||||
{
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return string.Format ("{0}, velocity: {1}, angle: {2}, start point: {3}, end point: {4}",
|
||||
base.ToString (), swipeVelocity, swipeAngle, startPoint, endPoint);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c11e6452bbdb4506bba74590e0ea981
|
||||
timeCreated: 1461834236
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,113 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
public class TKAnyTouchRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
public event Action<TKAnyTouchRecognizer> onEnteredEvent;
|
||||
public event Action<TKAnyTouchRecognizer> onExitedEvent;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// the contstructor ensures we have a frame to work with
|
||||
/// </summary>
|
||||
public TKAnyTouchRecognizer( TKRect frame )
|
||||
{
|
||||
alwaysSendTouchesMoved = true;
|
||||
boundaryFrame = frame;
|
||||
}
|
||||
|
||||
|
||||
void onTouchEntered()
|
||||
{
|
||||
// fire the event if this is the first touch we are tracking
|
||||
if( _trackingTouches.Count == 1 && onEnteredEvent != null )
|
||||
onEnteredEvent( this );
|
||||
}
|
||||
|
||||
|
||||
void onTouchExited()
|
||||
{
|
||||
if( _trackingTouches.Count == 0 && onExitedEvent != null )
|
||||
onExitedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
#region TKAbstractGestureRecognizer
|
||||
|
||||
// we do nothing here. all events will be handled internally
|
||||
internal override void fireRecognizedEvent() {}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
// grab the first touch that begins on us
|
||||
if( state == TKGestureRecognizerState.Possible )
|
||||
{
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
// only add touches in the Began phase
|
||||
if( touches[i].phase == TouchPhase.Began )
|
||||
{
|
||||
_trackingTouches.Add( touches[i] );
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
onTouchEntered();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
// check to see if the touch is in our frame
|
||||
var isTouchInFrame = isTouchWithinBoundaryFrame( touches[i] );
|
||||
|
||||
// are we already tracking this touch?
|
||||
var isTrackingTouch = _trackingTouches.Contains( touches[i] );
|
||||
|
||||
// if we are tracking the touch and it is in frame we do nothing more
|
||||
if( isTrackingTouch && isTouchInFrame )
|
||||
continue;
|
||||
|
||||
// if we are not tracking the touch and it is in our frame start tracking it
|
||||
if( !isTrackingTouch && isTouchInFrame )
|
||||
{
|
||||
_trackingTouches.Add( touches[i] );
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
onTouchEntered();
|
||||
}
|
||||
// if we are tracking the touch and it exited the frame fire the onExitedEvent
|
||||
else if( isTrackingTouch && !isTouchInFrame )
|
||||
{
|
||||
_trackingTouches.Remove( touches[i] );
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
onTouchExited();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if( touches[i].phase == TouchPhase.Ended && _trackingTouches.Contains( touches[i] ) )
|
||||
{
|
||||
_trackingTouches.Remove( touches[i] );
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
onTouchExited();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7aa8925dea2045e3be6e6676b65f7ac
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,152 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
public class TKButtonRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
public event Action<TKButtonRecognizer> onSelectedEvent;
|
||||
public event Action<TKButtonRecognizer> onDeselectedEvent;
|
||||
public event Action<TKButtonRecognizer> onTouchUpInsideEvent;
|
||||
|
||||
private TKRect _defaultFrame;
|
||||
private TKRect _highlightedFrame;
|
||||
|
||||
|
||||
/* State Definitions
|
||||
* Began: a touch started out on the button but has moved off. We still track it in case it comes back on the button
|
||||
* RecognizedAndStillRecognizing: a touch is currently down on the button
|
||||
* Recognized:
|
||||
*/
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// the constructors ensure we have a frame to work with for button recognizers
|
||||
/// </summary>
|
||||
public TKButtonRecognizer( TKRect defaultFrame ) : this( defaultFrame, 40f )
|
||||
{}
|
||||
|
||||
|
||||
public TKButtonRecognizer( TKRect defaultFrame, float highlightedExpansion ) : this( defaultFrame, defaultFrame.copyWithExpansion( highlightedExpansion ) )
|
||||
{}
|
||||
|
||||
|
||||
public TKButtonRecognizer( TKRect defaultFrame, TKRect highlightedFrame )
|
||||
{
|
||||
_defaultFrame = defaultFrame;
|
||||
_highlightedFrame = highlightedFrame;
|
||||
boundaryFrame = _defaultFrame;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Button methods. Subclasses can override these or you can skip subclassing and just listen to the events
|
||||
|
||||
/// <summary>
|
||||
/// called when a touch has began on the button or reentered the frame
|
||||
/// </summary>
|
||||
protected virtual void onSelected()
|
||||
{
|
||||
// while selected, we use a highlighted frame to allow the touch to move a bit and still remain selected
|
||||
boundaryFrame = _highlightedFrame;
|
||||
|
||||
if( onSelectedEvent != null )
|
||||
onSelectedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// called when a touch ends (if the button was already highlighted) or if a tracked touch exits the highlighted frame
|
||||
/// </summary>
|
||||
protected virtual void onDeselected()
|
||||
{
|
||||
if( onDeselectedEvent != null )
|
||||
onDeselectedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// called if a tracked touch ends while the button is highlighted
|
||||
/// </summary>
|
||||
protected virtual void onTouchUpInside()
|
||||
{
|
||||
if( onTouchUpInsideEvent != null )
|
||||
onTouchUpInsideEvent( this );
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region TKAbstractGestureRecognizer
|
||||
|
||||
// we do nothing here. all events will be handled internally
|
||||
internal override void fireRecognizedEvent() {}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
// grab the first touch that begins on us
|
||||
if( state == TKGestureRecognizerState.Possible )
|
||||
{
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
// only add touches in the Began phase
|
||||
if( touches[i].phase == TouchPhase.Began )
|
||||
{
|
||||
_trackingTouches.Add( touches[i] );
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
onSelected();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if( touches[i].phase == TouchPhase.Stationary )
|
||||
{
|
||||
// check to see if the touch is still in our frame
|
||||
var isTouchInFrame = isTouchWithinBoundaryFrame( touches[i] );
|
||||
|
||||
// if we are in the Began phase than we should switch to RecognizedAndStillRecognizing (highlighted) if the touch is in our frame
|
||||
if( state == TKGestureRecognizerState.Began && isTouchInFrame )
|
||||
{
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
onSelected();
|
||||
}
|
||||
else if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing && !isTouchInFrame ) // if the touch exits the frame and we were highlighted deselect now
|
||||
{
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
onDeselected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
// if we were previously highlighted (RecognizedAndStillRecognizing) we have an official touch
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
onTouchUpInside();
|
||||
|
||||
// reset the boundary frame
|
||||
boundaryFrame = _defaultFrame;
|
||||
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e4f7acc174154eb899d479350230e1c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,155 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
public class TKCurveRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
public event Action<TKCurveRecognizer> gestureRecognizedEvent;
|
||||
public event Action<TKCurveRecognizer> gestureCompleteEvent;
|
||||
|
||||
public float reportRotationStep = 20f; //how much rotation (degrees) is needed for the recognized event to fire
|
||||
public float squareDistance = 10f; //squared distance of touhes being evaluated
|
||||
public float maxSharpnes = 50f; //maximum angle (degrees) a touch is allowed to change direction of movement
|
||||
|
||||
public int minimumNumberOfTouches = 1;
|
||||
public int maximumNumberOfTouches = 2;
|
||||
|
||||
//should be read only
|
||||
public float deltaRotation = 0f; //rotation since last reported
|
||||
|
||||
private Vector2 _previousLocation;
|
||||
private Vector2 _deltaTranslation;//direction vector from previous to current location (current location being the last location far enough from the previous one)
|
||||
private Vector2 _previousDeltaTranslation;//last direction
|
||||
|
||||
|
||||
internal override void fireRecognizedEvent()
|
||||
{
|
||||
if( gestureRecognizedEvent != null )
|
||||
gestureRecognizedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
// add new or additional touches to gesture (allows for two or more touches to be added or removed without ending the pan gesture)
|
||||
if( state == TKGestureRecognizerState.Possible ||
|
||||
( ( state == TKGestureRecognizerState.Began || state == TKGestureRecognizerState.RecognizedAndStillRecognizing ) && _trackingTouches.Count < maximumNumberOfTouches ) )
|
||||
{
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
// only add touches in the Began phase
|
||||
if( touches[i].phase == TouchPhase.Began )
|
||||
{
|
||||
_trackingTouches.Add( touches[i] );
|
||||
|
||||
if( _trackingTouches.Count == maximumNumberOfTouches )
|
||||
break;
|
||||
}
|
||||
} // end for
|
||||
|
||||
if( _trackingTouches.Count >= minimumNumberOfTouches )
|
||||
{
|
||||
_previousLocation = touchLocation();
|
||||
if( state != TKGestureRecognizerState.RecognizedAndStillRecognizing ){
|
||||
//initialize values, but stay in Possible state. use Began only when there's enough data
|
||||
state = TKGestureRecognizerState.Possible;
|
||||
deltaRotation = 0f;
|
||||
_deltaTranslation = Vector2.zero;
|
||||
_previousDeltaTranslation = Vector2.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Possible ){
|
||||
//previous delta translation hasn't been set yet, need another itteration
|
||||
|
||||
Vector2 currentLocation = touchLocation();
|
||||
Vector2 delta = currentLocation - _previousLocation;
|
||||
_deltaTranslation = delta;
|
||||
_previousLocation = currentLocation;
|
||||
_previousDeltaTranslation = _deltaTranslation;
|
||||
|
||||
state = TKGestureRecognizerState.Began; //got enough data to begin recognizing in next move
|
||||
}
|
||||
else if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing || state == TKGestureRecognizerState.Began )
|
||||
{
|
||||
var currentLocation = touchLocation();
|
||||
|
||||
var delta = currentLocation - _previousLocation;
|
||||
if( delta.sqrMagnitude >= 10f )
|
||||
{
|
||||
var a = Vector2.Angle( _previousDeltaTranslation, delta );
|
||||
|
||||
if( a > maxSharpnes )
|
||||
{
|
||||
Debug.Log( "Curve is to sharp: " + a + " max sharpnes set to:" + maxSharpnes );
|
||||
state = TKGestureRecognizerState.FailedOrEnded; //calls reset
|
||||
}
|
||||
else
|
||||
{
|
||||
_deltaTranslation = delta;
|
||||
|
||||
float crossZ = Vector3.Cross( _previousDeltaTranslation, delta ).z;// / (_previousDeltaTranslation.magnitude * delta.magnitude);
|
||||
if( crossZ > 0f )
|
||||
deltaRotation -= a;
|
||||
else
|
||||
deltaRotation += a;
|
||||
|
||||
if( Mathf.Abs ( deltaRotation) >= reportRotationStep )
|
||||
{
|
||||
//if total rotation changed enough: recognize and reset total rotation
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing; // fires recognized event
|
||||
deltaRotation = 0f;//reset total rotation
|
||||
}
|
||||
_previousLocation = currentLocation;
|
||||
_previousDeltaTranslation = _deltaTranslation;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
// remove any completed touches
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if( touches[i].phase == TouchPhase.Ended )
|
||||
_trackingTouches.Remove( touches[i] );
|
||||
}
|
||||
|
||||
// if we still have a touch left continue. no touches means its time to reset
|
||||
if( _trackingTouches.Count >= minimumNumberOfTouches )
|
||||
{
|
||||
_previousLocation = touchLocation();
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing; //fires recognized event
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we had previously been recognizing fire our complete event
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
{
|
||||
if( gestureCompleteEvent != null )
|
||||
gestureCompleteEvent( this );
|
||||
}
|
||||
state = TKGestureRecognizerState.FailedOrEnded; //calls reset
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "[{0}] state: {1}, trans: {2}, lastTrans: {3}, totalRot: {4}", this.GetType(), state, _deltaTranslation, _previousDeltaTranslation, deltaRotation );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: daf4d976dc3d0492081da0f1b65c25b2
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,112 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// detects a long press. The gesture is considered recognized when a touch has been down for minimumPressDuration and if it has moved less than allowableMovement
|
||||
/// </summary>
|
||||
public class TKLongPressRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
public event Action<TKLongPressRecognizer> gestureRecognizedEvent;
|
||||
public event Action<TKLongPressRecognizer> gestureCompleteEvent; // fired when after a successful long press the finger is lifted
|
||||
|
||||
public float minimumPressDuration = 0.5f;
|
||||
public int requiredTouchesCount = -1;
|
||||
public float allowableMovementCm = 1f;
|
||||
|
||||
Vector2 _beginLocation;
|
||||
bool _waiting;
|
||||
|
||||
|
||||
|
||||
public TKLongPressRecognizer()
|
||||
{}
|
||||
|
||||
|
||||
public TKLongPressRecognizer( float minimumPressDuration, float allowableMovement, int requiredTouchesCount )
|
||||
{
|
||||
this.minimumPressDuration = minimumPressDuration;
|
||||
this.allowableMovementCm = allowableMovement;
|
||||
this.requiredTouchesCount = requiredTouchesCount;
|
||||
}
|
||||
|
||||
|
||||
IEnumerator beginGesture()
|
||||
{
|
||||
var endTime = Time.time + minimumPressDuration;
|
||||
|
||||
// wait for our time to elapse or to be cancelled
|
||||
while( _waiting && Time.time < endTime )
|
||||
yield return null;
|
||||
|
||||
// if our time elapsed it means we were not cancelled
|
||||
if( Time.time >= endTime )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Began )
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
}
|
||||
|
||||
_waiting = false;
|
||||
}
|
||||
|
||||
|
||||
internal override void fireRecognizedEvent()
|
||||
{
|
||||
if( gestureRecognizedEvent != null )
|
||||
gestureRecognizedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
if (!_waiting && state == TKGestureRecognizerState.Possible && (requiredTouchesCount == -1 || touches.Count == requiredTouchesCount))
|
||||
{
|
||||
_beginLocation = touches[0].position;
|
||||
_waiting = true;
|
||||
|
||||
TouchKit.instance.StartCoroutine( beginGesture() );
|
||||
_trackingTouches.Add(touches[0]);
|
||||
state = TKGestureRecognizerState.Began;
|
||||
}
|
||||
else if (requiredTouchesCount != -1)
|
||||
{
|
||||
_waiting = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Began || state == TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
{
|
||||
// did we move too far?
|
||||
var moveDistance = Vector2.Distance(touches[0].position, _beginLocation) / TouchKit.instance.ScreenPixelsPerCm;
|
||||
if (moveDistance > allowableMovementCm)
|
||||
{
|
||||
// fire the complete event if we had previously recognized a long press
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing && gestureCompleteEvent != null )
|
||||
gestureCompleteEvent( this );
|
||||
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
_waiting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
// fire the complete event if we had previously recognized a long press
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing && gestureCompleteEvent != null )
|
||||
gestureCompleteEvent( this );
|
||||
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
_waiting = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1116f36a0f51547ab8db2bcc051d3662
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,68 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// detects a rotation around an object with a single finger. The target objects position must be provided in screen coordinates.
|
||||
/// </summary>
|
||||
public class TKOneFingerRotationRecognizer : TKRotationRecognizer
|
||||
{
|
||||
public new event Action<TKOneFingerRotationRecognizer> gestureRecognizedEvent;
|
||||
public new event Action<TKOneFingerRotationRecognizer> gestureCompleteEvent;
|
||||
|
||||
/// <summary>
|
||||
/// this should be the center point in screen coordinates of the object that is being rotated
|
||||
/// </summary>
|
||||
public Vector2 targetPosition;
|
||||
|
||||
|
||||
internal override void fireRecognizedEvent()
|
||||
{
|
||||
if( gestureRecognizedEvent != null )
|
||||
gestureRecognizedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Possible )
|
||||
{
|
||||
_trackingTouches.Add( touches[0] );
|
||||
|
||||
deltaRotation = 0;
|
||||
_previousRotation = angleBetweenPoints( targetPosition, _trackingTouches[0].position );
|
||||
state = TKGestureRecognizerState.Began;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing || state == TKGestureRecognizerState.Began )
|
||||
{
|
||||
var currentRotation = angleBetweenPoints( targetPosition, _trackingTouches[0].position );
|
||||
deltaRotation = Mathf.DeltaAngle( currentRotation, _previousRotation );
|
||||
_previousRotation = currentRotation;
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
// if we had previously been recognizing fire our complete event
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
{
|
||||
if( gestureCompleteEvent != null )
|
||||
gestureCompleteEvent( this );
|
||||
}
|
||||
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2924e17256fe946bcad4a3f8d5a8abcc
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,160 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
public class TKPanRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
public event Action<TKPanRecognizer> gestureRecognizedEvent;
|
||||
public event Action<TKPanRecognizer> gestureCompleteEvent;
|
||||
|
||||
public Vector2 deltaTranslation;
|
||||
public float deltaTranslationCm;
|
||||
public int minimumNumberOfTouches = 1;
|
||||
public int maximumNumberOfTouches = 2;
|
||||
|
||||
private float totalDeltaMovementInCm = 0f;
|
||||
private Vector2 _previousLocation;
|
||||
private float _minDistanceToPanCm;
|
||||
private Vector2 _startPoint;
|
||||
private Vector2 _endPoint;
|
||||
|
||||
|
||||
public Vector2 startPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._startPoint;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 endPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._endPoint;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public TKPanRecognizer( float minPanDistanceCm = 0.5f )
|
||||
{
|
||||
_minDistanceToPanCm = minPanDistanceCm;
|
||||
}
|
||||
|
||||
|
||||
internal override void fireRecognizedEvent()
|
||||
{
|
||||
if( gestureRecognizedEvent != null )
|
||||
gestureRecognizedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
// extra touches abort gesture
|
||||
if( _trackingTouches.Count + touches.Count > maximumNumberOfTouches )
|
||||
{
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
return false;
|
||||
}
|
||||
|
||||
// add new or additional touches to gesture (allows for two or more touches to be added or removed without ending the pan gesture)
|
||||
if( state == TKGestureRecognizerState.Possible || ( ( state == TKGestureRecognizerState.Began || state == TKGestureRecognizerState.RecognizedAndStillRecognizing ) && _trackingTouches.Count < maximumNumberOfTouches ) )
|
||||
{
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
// only add touches in the Began phase
|
||||
if( touches[i].phase == TouchPhase.Began )
|
||||
{
|
||||
_trackingTouches.Add( touches[i] );
|
||||
_startPoint = touches[0].position;
|
||||
|
||||
if( _trackingTouches.Count == maximumNumberOfTouches )
|
||||
break;
|
||||
}
|
||||
} // end for
|
||||
|
||||
if( _trackingTouches.Count >= minimumNumberOfTouches && _trackingTouches.Count <= maximumNumberOfTouches )
|
||||
{
|
||||
_previousLocation = touchLocation();
|
||||
if( state != TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
{
|
||||
totalDeltaMovementInCm = 0f;
|
||||
state = TKGestureRecognizerState.Began;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
//do not engage with touch events if the number of touches is outside our desired constraints
|
||||
if( _trackingTouches.Count >= minimumNumberOfTouches && _trackingTouches.Count <= maximumNumberOfTouches )
|
||||
{
|
||||
var currentLocation = touchLocation();
|
||||
deltaTranslation = currentLocation - _previousLocation;
|
||||
deltaTranslationCm = deltaTranslation.magnitude / TouchKit.instance.ScreenPixelsPerCm;
|
||||
_previousLocation = currentLocation;
|
||||
|
||||
if( state == TKGestureRecognizerState.Began )
|
||||
{
|
||||
totalDeltaMovementInCm += deltaTranslationCm;
|
||||
|
||||
if( Math.Abs( totalDeltaMovementInCm ) >= _minDistanceToPanCm )
|
||||
{
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
_endPoint = touchLocation();
|
||||
|
||||
// remove any completed touches
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if( touches[i].phase == TouchPhase.Ended )
|
||||
_trackingTouches.Remove( touches[i] );
|
||||
}
|
||||
|
||||
// if we still have a touch left continue. no touches means its time to reset
|
||||
if( _trackingTouches.Count >= minimumNumberOfTouches )
|
||||
{
|
||||
_previousLocation = touchLocation();
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we had previously been recognizing fire our complete event
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
{
|
||||
if( gestureCompleteEvent != null )
|
||||
{
|
||||
gestureCompleteEvent( this );
|
||||
}
|
||||
}
|
||||
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "[{0}] state: {1}, location: {2}, deltaTranslation: {3}", this.GetType(), state, touchLocation(), deltaTranslation );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 675d9465bff894932a61c4ab70006c02
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,147 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
public class TKPinchRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
public event Action<TKPinchRecognizer> gestureRecognizedEvent;
|
||||
public event Action<TKPinchRecognizer> gestureCompleteEvent;
|
||||
|
||||
/// <summary>
|
||||
/// the minimum amount of distance the two fingers must move apart before the gesture is recognized
|
||||
/// </summary>
|
||||
public float minimumScaleDistanceToRecognize = 0;
|
||||
public float deltaScale = 0;
|
||||
|
||||
private float _intialDistance = 0; // represents the distance between two fingers when gesture was first officially recognized
|
||||
private float _firstDistance = 0; // first ever distance when two fingers hit the screen (not yet recognized)
|
||||
private float _previousDistance = 0;
|
||||
|
||||
/// <summary>
|
||||
/// calculated, read-only property. Represents the scale accumulated since the gesture was initially recognized
|
||||
/// </summary>
|
||||
/// <value>The accumulated scale.</value>
|
||||
public float accumulatedScale
|
||||
{
|
||||
get
|
||||
{
|
||||
var currentDistance = distanceBetweenTrackedTouches();
|
||||
return currentDistance / _intialDistance;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private float distanceBetweenTrackedTouches()
|
||||
{
|
||||
// prevent NaN when the distance between the touches is zero -- only happens in editor
|
||||
var distance = Vector2.Distance( _trackingTouches[0].position, _trackingTouches[1].position );
|
||||
return ( Mathf.Max(0.0001f, distance) / TouchKit.instance.ScreenPixelsPerCm );
|
||||
}
|
||||
|
||||
|
||||
internal override void fireRecognizedEvent()
|
||||
{
|
||||
if( gestureRecognizedEvent != null )
|
||||
gestureRecognizedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Possible )
|
||||
{
|
||||
// we need to have two touches to work with so we dont set state to Begin until then
|
||||
// latch the touches
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
// only add touches in the Began phase
|
||||
if( touches[i].phase == TouchPhase.Began )
|
||||
{
|
||||
_trackingTouches.Add( touches[i] );
|
||||
|
||||
if( _trackingTouches.Count == 2 )
|
||||
break;
|
||||
}
|
||||
} // end for
|
||||
|
||||
if( _trackingTouches.Count == 2 )
|
||||
{
|
||||
// gesture cannot be recognized until the two touches exceed the minimum scale threshold
|
||||
_firstDistance = distanceBetweenTrackedTouches();
|
||||
|
||||
// if( minimumScaleDistanceToRecognize == 0 )
|
||||
// {
|
||||
// _intialDistance = _firstDistance;
|
||||
// state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
// if the two fingers move far apart to exceed the minimum threshold, begin officially recognizing the gesture
|
||||
if( _trackingTouches.Count == 2 )
|
||||
{
|
||||
if ( state == TKGestureRecognizerState.Possible )
|
||||
{
|
||||
if( Mathf.Abs( distanceBetweenTrackedTouches() - _firstDistance ) >= minimumScaleDistanceToRecognize )
|
||||
{
|
||||
deltaScale = 0;
|
||||
_intialDistance = distanceBetweenTrackedTouches();
|
||||
_previousDistance = _intialDistance;
|
||||
state = TKGestureRecognizerState.Began;
|
||||
}
|
||||
}
|
||||
else if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing || state == TKGestureRecognizerState.Began )
|
||||
{
|
||||
var currentDistance = distanceBetweenTrackedTouches();
|
||||
deltaScale = ( currentDistance - _previousDistance ) / _intialDistance;
|
||||
_previousDistance = currentDistance;
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
// remove any completed touches
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if( touches[i].phase == TouchPhase.Ended )
|
||||
_trackingTouches.Remove( touches[i] );
|
||||
}
|
||||
|
||||
// if we had previously been recognizing fire our complete event
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
{
|
||||
if( gestureCompleteEvent != null )
|
||||
gestureCompleteEvent( this );
|
||||
}
|
||||
|
||||
// if we still have a touch left continue to wait for another. no touches means its time to reset
|
||||
if( _trackingTouches.Count == 1 )
|
||||
{
|
||||
state = TKGestureRecognizerState.Possible;
|
||||
deltaScale = 0; // I don't know why this would be 1 instead of 0
|
||||
}
|
||||
else
|
||||
{
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "[{0}] state: {1}, deltaScale: {2}", this.GetType(), state, deltaScale );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fbc0b12876354ebb9ebe2a28e3d9709
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,160 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class TKRotationRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
public event Action<TKRotationRecognizer> gestureRecognizedEvent;
|
||||
public event Action<TKRotationRecognizer> gestureCompleteEvent;
|
||||
|
||||
public float deltaRotation = 0;
|
||||
public float minimumRotationToRecognize = 0;
|
||||
|
||||
protected float _previousRotation = 0;
|
||||
protected float _firstRotation = 0; // first ever rotation angle when 2 fingers hit the screen
|
||||
protected float _initialRotation = 0; // rotation angle when gesture is officially recognized
|
||||
|
||||
public float accumulatedRotation
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _trackingTouches.Count == 2 )
|
||||
return Mathf.DeltaAngle( angleBetweenPoints( _trackingTouches[0].position, _trackingTouches[1].position ), _initialRotation );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// this is public due to its usefulness elsewhere. it should probably move to a helper class.
|
||||
/// </summary>
|
||||
public static float angleBetweenPoints( Vector2 position1, Vector2 position2 )
|
||||
{
|
||||
var fromLine = position2 - position1;
|
||||
var toLine = new Vector2( 1, 0 );
|
||||
|
||||
var angle = Vector2.Angle( fromLine, toLine );
|
||||
var cross = Vector3.Cross( fromLine, toLine );
|
||||
|
||||
// did we wrap around?
|
||||
if( cross.z > 0 )
|
||||
angle = 360f - angle;
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
|
||||
internal override void fireRecognizedEvent()
|
||||
{
|
||||
if( gestureRecognizedEvent != null )
|
||||
gestureRecognizedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Possible )
|
||||
{
|
||||
// we need to have two touches to work with so we dont set state to Begin until then
|
||||
// latch the touches
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
// only add touches in the Began phase
|
||||
if( touches[i].phase == TouchPhase.Began )
|
||||
{
|
||||
_trackingTouches.Add( touches[i] );
|
||||
|
||||
if( _trackingTouches.Count == 2 )
|
||||
break;
|
||||
}
|
||||
} // end for
|
||||
|
||||
if( _trackingTouches.Count == 2 )
|
||||
{
|
||||
if( minimumRotationToRecognize == 0 )
|
||||
{
|
||||
deltaRotation = 0;
|
||||
_previousRotation = angleBetweenPoints( _trackingTouches[0].position, _trackingTouches[1].position );
|
||||
state = TKGestureRecognizerState.Began;
|
||||
}
|
||||
else
|
||||
{
|
||||
// gesture cannot be recognized until the the rotation angle exceeds the minimum threshold
|
||||
_firstRotation = angleBetweenPoints( _trackingTouches[0].position, _trackingTouches[1].position );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
// if the gesture has passed the minimum delta rotation threshold, begin officially recognizing the gesture
|
||||
if( state == TKGestureRecognizerState.Possible && _trackingTouches.Count == 2 )
|
||||
{
|
||||
var cr = angleBetweenPoints( _trackingTouches[0].position, _trackingTouches[1].position );
|
||||
var delta = Mathf.Abs( Mathf.DeltaAngle( cr, _firstRotation ) );
|
||||
|
||||
if( delta > minimumRotationToRecognize )
|
||||
{
|
||||
_initialRotation = cr; // the starting rotation when the gesture was first recognized
|
||||
|
||||
deltaRotation = 0;
|
||||
_previousRotation = angleBetweenPoints( _trackingTouches[0].position, _trackingTouches[1].position );
|
||||
state = TKGestureRecognizerState.Began;
|
||||
}
|
||||
}
|
||||
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing || state == TKGestureRecognizerState.Began )
|
||||
{
|
||||
var currentRotation = angleBetweenPoints( _trackingTouches[0].position, _trackingTouches[1].position );
|
||||
deltaRotation = Mathf.DeltaAngle( currentRotation, _previousRotation );
|
||||
_previousRotation = currentRotation;
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
// remove any completed touches
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if( touches[i].phase == TouchPhase.Ended )
|
||||
_trackingTouches.Remove( touches[i] );
|
||||
}
|
||||
|
||||
// if we had previously been recognizing fire our complete event
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
{
|
||||
if( gestureCompleteEvent != null )
|
||||
gestureCompleteEvent( this );
|
||||
}
|
||||
|
||||
// if we still have a touch left continue to wait for another. no touches means its time to reset
|
||||
if( _trackingTouches.Count == 1 )
|
||||
{
|
||||
state = TKGestureRecognizerState.Possible;
|
||||
deltaRotation = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
_initialRotation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "[{0}] state: {1}, location: {2}, rotation: {3}", this.GetType(), state, touchLocation(), deltaRotation );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c732a760266d044c28bd68c3db8bd88d
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,257 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
[System.Flags]
|
||||
public enum TKSwipeDirection
|
||||
{
|
||||
Left = ( 1 << 0 ),
|
||||
Right = ( 1 << 1 ),
|
||||
Up = ( 1 << 2 ),
|
||||
Down = ( 1 << 3 ),
|
||||
|
||||
UpLeft = ( 1 << 4 ),
|
||||
DownLeft = ( 1 << 5 ),
|
||||
UpRight = ( 1 << 6 ),
|
||||
DownRight = ( 1 << 7 ),
|
||||
|
||||
Horizontal = ( Left | Right ),
|
||||
Vertical = ( Up | Down ),
|
||||
Cardinal = ( Horizontal | Vertical ),
|
||||
|
||||
DiagonalUp = ( UpLeft | UpRight ),
|
||||
DiagonalDown = ( DownLeft | DownRight ),
|
||||
DiagonalLeft = ( UpLeft | DownLeft ),
|
||||
DiagonalRight = ( UpRight | DownRight ),
|
||||
Diagonal = ( DiagonalUp | DiagonalDown ),
|
||||
|
||||
RightSide = ( Right | DiagonalRight ),
|
||||
LeftSide = ( Left | DiagonalLeft ),
|
||||
TopSide = ( Up | DiagonalUp ),
|
||||
BottomSide = ( Down | DiagonalDown ),
|
||||
|
||||
All = ( Horizontal | Vertical | Diagonal )
|
||||
}
|
||||
|
||||
public class TKSwipeRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
/// <summary>
|
||||
/// The event that fires when a swipe is recognized.
|
||||
/// </summary>
|
||||
public event System.Action<TKSwipeRecognizer> gestureRecognizedEvent;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of time for the motion to be considered a swipe.
|
||||
/// Setting to 0f will disable the time restriction completely.
|
||||
/// </summary>
|
||||
public float timeToSwipe = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The velocity of the swipe, in centimeters based on the screen resolution
|
||||
/// and pixel density, if available.
|
||||
/// </summary>
|
||||
public float swipeVelocity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The direction that the swipe was made in. Possibilities include the four
|
||||
/// cardinal directions and the four diagonal directions.
|
||||
/// </summary>
|
||||
public TKSwipeDirection completedSwipeDirection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The minimum number of simultaneous touches (fingers) on the screen to trigger
|
||||
/// this swipe recognizer. Default is 1.
|
||||
/// </summary>
|
||||
public int minimumNumberOfTouches = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of simultaneous touches (fingers) on the screen to trigger
|
||||
/// this swipe recognizer. Default is 2.
|
||||
/// </summary>
|
||||
public int maximumNumberOfTouches = 2;
|
||||
|
||||
/// <summary>
|
||||
/// If true, will trigger on the frame that the criteria for a swipe are first met.
|
||||
/// If false, will only trigger on completion of the motion, when the touch is lifted.
|
||||
/// </summary>
|
||||
public bool triggerWhenCriteriaMet = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The minimum distance in centimeters that the gesture has to make to be considered
|
||||
/// a proper swipe, based on resolution and pixel density. Default is 2cm.
|
||||
/// </summary>
|
||||
private float _minimumDistance = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum distance in centimeters that the gesture has to make to be considered
|
||||
/// a proper swipe.
|
||||
/// </summary>
|
||||
private float _maximumDistance = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// The individual points that make up the gesture, recorded every frame from when a
|
||||
/// finger is first pressed to the screen until it's lifted. Only tracks the first touch
|
||||
/// on the screen, in the case of multiple touches.
|
||||
/// </summary>
|
||||
private List<Vector2> _points = new List<Vector2>();
|
||||
|
||||
/// <summary>
|
||||
/// The time that the gesture started. Is used to determine if the time limit has been
|
||||
/// passed, and whether to ignore further checks.
|
||||
/// </summary>
|
||||
private float _startTime;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The first touch point in the gesture.
|
||||
/// </summary>
|
||||
public Vector2 startPoint
|
||||
{
|
||||
get { return this._points.FirstOrDefault(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The last touch point in the gesture.
|
||||
/// </summary>
|
||||
public Vector2 endPoint
|
||||
{
|
||||
get { return this._points.LastOrDefault(); }
|
||||
}
|
||||
|
||||
|
||||
public TKSwipeRecognizer() : this( 2f, 10f )
|
||||
{ }
|
||||
|
||||
public TKSwipeRecognizer( float minimumDistanceCm, float maximumDistanceCm )
|
||||
{
|
||||
this._minimumDistance = minimumDistanceCm;
|
||||
this._maximumDistance = maximumDistanceCm;
|
||||
}
|
||||
|
||||
|
||||
private bool checkForSwipeCompletion( TKTouch touch )
|
||||
{
|
||||
// if we have a time stipulation and we exceeded it stop listening for swipes, fail
|
||||
if( timeToSwipe > 0.0f && ( Time.time - this._startTime ) > timeToSwipe )
|
||||
return false;
|
||||
|
||||
// if we don't have at least two points to test yet, then fail
|
||||
if( this._points.Count < 2 )
|
||||
return false;
|
||||
|
||||
// the ideal distance in pixels from the start to the finish
|
||||
float idealDistance = Vector2.Distance( startPoint, endPoint );
|
||||
|
||||
// the ideal distance in centimeters, based on the screen pixel density
|
||||
float idealDistanceCM = idealDistance / TouchKit.instance.ScreenPixelsPerCm;
|
||||
|
||||
// if the distance moved in cm was less than the minimum,
|
||||
if( idealDistanceCM < this._minimumDistance || idealDistanceCM > this._maximumDistance )
|
||||
return false;
|
||||
|
||||
// add up distances between all points sampled during the gesture to get the real distance
|
||||
float realDistance = 0f;
|
||||
for( int i = 1; i < this._points.Count; i++ )
|
||||
realDistance += Vector2.Distance( this._points[i], this._points[i - 1] );
|
||||
|
||||
// if the real distance is 10% greater than the ideal distance, then fail
|
||||
// this weeds out really irregular "lines" and curves from being considered swipes
|
||||
if( realDistance > idealDistance * 1.1f )
|
||||
return false;
|
||||
|
||||
// the speed in cm/s of the swipe
|
||||
swipeVelocity = idealDistanceCM / ( Time.time - this._startTime );
|
||||
|
||||
// turn the slope of the ideal swipe line into an angle in degrees
|
||||
Vector2 v2 = ( endPoint - startPoint ).normalized;
|
||||
float swipeAngle = Mathf.Atan2( v2.y, v2.x ) * Mathf.Rad2Deg;
|
||||
if( swipeAngle < 0 )
|
||||
swipeAngle = 360 + swipeAngle;
|
||||
swipeAngle = 360 - swipeAngle;
|
||||
|
||||
// depending on the angle of the line, give a logical swipe direction
|
||||
if( swipeAngle >= 292.5f && swipeAngle <= 337.5f )
|
||||
completedSwipeDirection = TKSwipeDirection.UpRight;
|
||||
else if( swipeAngle >= 247.5f && swipeAngle <= 292.5f )
|
||||
completedSwipeDirection = TKSwipeDirection.Up;
|
||||
else if( swipeAngle >= 202.5f && swipeAngle <= 247.5f )
|
||||
completedSwipeDirection = TKSwipeDirection.UpLeft;
|
||||
else if( swipeAngle >= 157.5f && swipeAngle <= 202.5f )
|
||||
completedSwipeDirection = TKSwipeDirection.Left;
|
||||
else if( swipeAngle >= 112.5f && swipeAngle <= 157.5f )
|
||||
completedSwipeDirection = TKSwipeDirection.DownLeft;
|
||||
else if( swipeAngle >= 67.5f && swipeAngle <= 112.5f )
|
||||
completedSwipeDirection = TKSwipeDirection.Down;
|
||||
else if( swipeAngle >= 22.5f && swipeAngle <= 67.5f )
|
||||
completedSwipeDirection = TKSwipeDirection.DownRight;
|
||||
else // swipeAngle >= 337.5f || swipeAngle <= 22.5f
|
||||
completedSwipeDirection = TKSwipeDirection.Right;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal override void fireRecognizedEvent()
|
||||
{
|
||||
if( gestureRecognizedEvent != null )
|
||||
gestureRecognizedEvent( this );
|
||||
}
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Possible )
|
||||
{
|
||||
// add any touches on screen
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
this._trackingTouches.Add( touches[i] );
|
||||
|
||||
// if the number of touches is within our constraints, begin tracking
|
||||
if( this._trackingTouches.Count >= minimumNumberOfTouches && this._trackingTouches.Count <= maximumNumberOfTouches )
|
||||
{
|
||||
// reset everything
|
||||
this._points.Clear();
|
||||
this._points.Add( touches[0].position );
|
||||
|
||||
this._startTime = Time.time;
|
||||
state = TKGestureRecognizerState.Began;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
// only bother doing anything if we haven't recognized or failed yet
|
||||
if( state == TKGestureRecognizerState.Began )
|
||||
{
|
||||
this._points.Add( touches[0].position );
|
||||
|
||||
// if we're triggering when the criteria is met, then check for completion every frame
|
||||
if( triggerWhenCriteriaMet && checkForSwipeCompletion( touches[0] ) )
|
||||
state = TKGestureRecognizerState.Recognized;
|
||||
}
|
||||
}
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
// if we haven't recognized or failed yet
|
||||
if( state == TKGestureRecognizerState.Began )
|
||||
{
|
||||
this._points.Add( touches[0].position );
|
||||
|
||||
// last frame, one last check- recognized or fail
|
||||
if( checkForSwipeCompletion( touches[0] ) )
|
||||
state = TKGestureRecognizerState.Recognized;
|
||||
else
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "{0}, swipe direction: {1}, swipe velocity: {2}, start point: {3}, end point: {4}",
|
||||
base.ToString(), completedSwipeDirection, swipeVelocity, startPoint, endPoint );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05051c1d2a36c43a195c3a390af1b74a
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,107 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
public class TKTapRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
public event Action<TKTapRecognizer> gestureRecognizedEvent;
|
||||
|
||||
public int numberOfTapsRequired = 1;
|
||||
public int numberOfTouchesRequired = 1;
|
||||
|
||||
// taps that last longer than this duration will be ignored
|
||||
float _maxDurationForTapConsideration = 0.5f;
|
||||
|
||||
float _maxDeltaMovementForTapConsideration = 1f;
|
||||
float _touchBeganTime;
|
||||
int _preformedTapsCount = 0;
|
||||
|
||||
|
||||
public TKTapRecognizer() : this( 0.5f, 1f )
|
||||
{}
|
||||
|
||||
|
||||
public TKTapRecognizer( float maxDurationForTapConsideration, float maxDeltaMovementForTapConsiderationCm )
|
||||
{
|
||||
_maxDurationForTapConsideration = maxDurationForTapConsideration;
|
||||
_maxDeltaMovementForTapConsideration = maxDeltaMovementForTapConsiderationCm;
|
||||
}
|
||||
|
||||
|
||||
internal override void fireRecognizedEvent()
|
||||
{
|
||||
if( gestureRecognizedEvent != null )
|
||||
gestureRecognizedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
if( Time.time > _touchBeganTime + _maxDurationForTapConsideration && _preformedTapsCount != 0 && _preformedTapsCount < numberOfTapsRequired )
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
|
||||
if( state == TKGestureRecognizerState.Possible )
|
||||
{
|
||||
for( int i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
// only add touches in the Began phase
|
||||
if( touches[i].phase == TouchPhase.Began )
|
||||
{
|
||||
_trackingTouches.Add( touches[i] );
|
||||
|
||||
if( _trackingTouches.Count == numberOfTouchesRequired )
|
||||
break;
|
||||
}
|
||||
} // end for
|
||||
|
||||
if( _trackingTouches.Count == numberOfTouchesRequired )
|
||||
{
|
||||
_touchBeganTime = Time.time;
|
||||
_preformedTapsCount = 0;
|
||||
state = TKGestureRecognizerState.Began;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Began )
|
||||
{
|
||||
// did we move?
|
||||
for( var i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if (
|
||||
((Math.Abs(touches[i].position.x - touches[i].startPosition.x) / TouchKit.instance.ScreenPixelsPerCm) > _maxDeltaMovementForTapConsideration) ||
|
||||
((Math.Abs(touches[i].position.y - touches[i].startPosition.y) / TouchKit.instance.ScreenPixelsPerCm) > _maxDeltaMovementForTapConsideration)
|
||||
)
|
||||
{
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Began && ( Time.time <= _touchBeganTime + _maxDurationForTapConsideration ) )
|
||||
{
|
||||
++_preformedTapsCount;
|
||||
if( _preformedTapsCount == numberOfTapsRequired )
|
||||
state = TKGestureRecognizerState.Recognized;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e33f2100ab364510815a15f9fc0503d
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,103 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
public class TKTouchPadRecognizer : TKAbstractGestureRecognizer
|
||||
{
|
||||
public event Action<TKTouchPadRecognizer> gestureRecognizedEvent;
|
||||
public event Action<TKTouchPadRecognizer> gestureCompleteEvent;
|
||||
|
||||
public AnimationCurve inputCurve = AnimationCurve.Linear( 0.0f, 0.0f, 1.0f, 1.0f );
|
||||
public Vector2 value;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// the constructor ensures we have a frame to work with for this recognizer
|
||||
/// </summary>
|
||||
public TKTouchPadRecognizer( TKRect frame )
|
||||
{
|
||||
boundaryFrame = frame;
|
||||
}
|
||||
|
||||
|
||||
internal override void fireRecognizedEvent()
|
||||
{
|
||||
if( gestureRecognizedEvent != null )
|
||||
gestureRecognizedEvent( this );
|
||||
}
|
||||
|
||||
|
||||
internal override bool touchesBegan( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.Possible )
|
||||
{
|
||||
for( var i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
// only add touches in the Began phase
|
||||
if( touches[i].phase == TouchPhase.Began )
|
||||
_trackingTouches.Add( touches[i] );
|
||||
}
|
||||
|
||||
if( _trackingTouches.Count > 0 )
|
||||
{
|
||||
state = TKGestureRecognizerState.Began;
|
||||
|
||||
// call through to touchesMoved so we set the value and set the state to RecognizedAndStillRecognizing which triggers the recognized event
|
||||
touchesMoved( touches );
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesMoved( List<TKTouch> touches )
|
||||
{
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing || state == TKGestureRecognizerState.Began )
|
||||
{
|
||||
var currentLocation = touchLocation();
|
||||
value = currentLocation - boundaryFrame.Value.center;
|
||||
|
||||
// normalize from 0 - 1 and clamp
|
||||
value.x = Mathf.Clamp( value.x / ( boundaryFrame.Value.width * 0.5f ), -1f, 1f );
|
||||
value.y = Mathf.Clamp( value.y / ( boundaryFrame.Value.height * 0.5f ), -1f, 1f );
|
||||
|
||||
// apply our inputCurve
|
||||
value.x = inputCurve.Evaluate( Mathf.Abs( value.x ) ) * Mathf.Sign( value.x );
|
||||
value.y = inputCurve.Evaluate( Mathf.Abs( value.y ) ) * Mathf.Sign( value.y );
|
||||
|
||||
state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override void touchesEnded( List<TKTouch> touches )
|
||||
{
|
||||
// remove any completed touches
|
||||
for( var i = 0; i < touches.Count; i++ )
|
||||
{
|
||||
if( touches[i].phase == TouchPhase.Ended )
|
||||
_trackingTouches.Remove( touches[i] );
|
||||
}
|
||||
|
||||
// if we had previously been recognizing fire our complete event
|
||||
if( state == TKGestureRecognizerState.RecognizedAndStillRecognizing )
|
||||
{
|
||||
if( gestureCompleteEvent != null )
|
||||
gestureCompleteEvent( this );
|
||||
}
|
||||
|
||||
value = Vector2.zero;
|
||||
state = TKGestureRecognizerState.FailedOrEnded;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "[{0}] state: {1}, value: {2}", this.GetType(), state, value );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 884f51553239d423d859e6adb04b1fc8
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,308 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
public partial class TouchKit : MonoBehaviour
|
||||
{
|
||||
[HideInInspector]
|
||||
public bool simulateTouches = true;
|
||||
[HideInInspector]
|
||||
public bool simulateMultitouch = true;
|
||||
[HideInInspector]
|
||||
public bool drawTouches = false;
|
||||
[HideInInspector]
|
||||
public bool drawDebugBoundaryFrames = false;
|
||||
|
||||
/// <summary>
|
||||
/// lets TouchKit know if it should scale all rects and distances based on the designTimeResolution
|
||||
/// </summary>
|
||||
public bool autoScaleRectsAndDistances = true;
|
||||
|
||||
/// <summary>
|
||||
/// if false, TouchKit will not do anything in Update. You will need to call updateTouches yourself.
|
||||
/// </summary>
|
||||
public bool shouldAutoUpdateTouches = true;
|
||||
|
||||
private Vector2 _designTimeResolution = new Vector2( 320, 180 ); // 16:9 is a decent starting point for aspect ratio
|
||||
/// <summary>
|
||||
/// all TKRect sizes should be based on this screen size. They will be adjusted at runtime if autoUpdateRects is true
|
||||
/// </summary>
|
||||
public Vector2 designTimeResolution {
|
||||
get {
|
||||
return _designTimeResolution;
|
||||
}
|
||||
set {
|
||||
_designTimeResolution = value;
|
||||
setupRuntimeScale();
|
||||
}
|
||||
}
|
||||
public int maxTouchesToProcess = 2;
|
||||
|
||||
/// <summary>
|
||||
/// used at runtime to scale any TKRects as they are made for the current screen size
|
||||
/// </summary>
|
||||
public Vector2 runtimeScaleModifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// used at runtime to modify distances
|
||||
/// </summary>
|
||||
public float runtimeDistanceModifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// used at runtime to translate a pixel value into Unity units. Just multiply the pixel value by the pixelsToUnityUnitsMultiplier.
|
||||
/// Note that this will only work for orthographic cameras!
|
||||
/// </summary>
|
||||
public Vector2 pixelsToUnityUnitsMultiplier { get; private set; }
|
||||
|
||||
|
||||
private List<TKAbstractGestureRecognizer> _gestureRecognizers = new List<TKAbstractGestureRecognizer>( 5 );
|
||||
private TKTouch[] _touchCache;
|
||||
private List<TKTouch> _liveTouches = new List<TKTouch>( 2 );
|
||||
private bool _shouldCheckForLostTouches = false; // used internally to ensure we dont check for lost touches too often
|
||||
|
||||
private const float inchesToCentimeters = 2.54f;
|
||||
|
||||
public float ScreenPixelsPerCm
|
||||
{
|
||||
get
|
||||
{
|
||||
float fallbackDpi = 72f;
|
||||
#if UNITY_ANDROID
|
||||
// Android MDPI setting fallback
|
||||
// http://developer.android.com/guide/practices/screens_support.html
|
||||
fallbackDpi = 160f;
|
||||
#elif (UNITY_WP8 || UNITY_WP8_1 || UNITY_WSA || UNITY_WSA_8_0)
|
||||
// Windows phone is harder to track down
|
||||
// http://www.windowscentral.com/higher-resolution-support-windows-phone-7-dpi-262
|
||||
fallbackDpi = 92f;
|
||||
#elif UNITY_IOS
|
||||
// iPhone 4-6 range
|
||||
fallbackDpi = 326f;
|
||||
#endif
|
||||
|
||||
return Screen.dpi == 0f ? fallbackDpi / inchesToCentimeters : Screen.dpi / inchesToCentimeters;
|
||||
}
|
||||
}
|
||||
|
||||
private static TouchKit _instance = null;
|
||||
public static TouchKit instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if( !_instance )
|
||||
{
|
||||
// check if there is a GO instance already available in the scene graph
|
||||
_instance = FindObjectOfType( typeof( TouchKit ) ) as TouchKit;
|
||||
|
||||
// nope, create a new one
|
||||
if( !_instance )
|
||||
{
|
||||
var obj = new GameObject( "TouchKit" );
|
||||
_instance = obj.AddComponent<TouchKit>();
|
||||
DontDestroyOnLoad( obj );
|
||||
}
|
||||
|
||||
// prep the scalers. for the distance scaler we just use an average of the width and height scales
|
||||
var aCamera = Camera.main ?? Camera.allCameras[0];
|
||||
if( aCamera.orthographic )
|
||||
{
|
||||
setupPixelsToUnityUnitsMultiplierWithCamera( aCamera );
|
||||
}
|
||||
else
|
||||
{
|
||||
_instance.pixelsToUnityUnitsMultiplier = Vector2.one;
|
||||
}
|
||||
|
||||
_instance.setupRuntimeScale();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupRuntimeScale()
|
||||
{
|
||||
_instance.runtimeScaleModifier = new Vector2( Screen.width / _instance.designTimeResolution.x, Screen.height / _instance.designTimeResolution.y );
|
||||
_instance.runtimeDistanceModifier = ( _instance.runtimeScaleModifier.x + _instance.runtimeScaleModifier.y ) / 2f;
|
||||
|
||||
if( !_instance.autoScaleRectsAndDistances )
|
||||
{
|
||||
_instance.runtimeScaleModifier = Vector2.one;
|
||||
_instance.runtimeDistanceModifier = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity often misses the Ended phase of touches so this method will look out for that
|
||||
/// </summary>
|
||||
private void addTouchesUnityForgotToEndToLiveTouchesList()
|
||||
{
|
||||
for( int i = 0; i < _touchCache.Length; i++ )
|
||||
{
|
||||
if( _touchCache[i].phase != TouchPhase.Ended )
|
||||
{
|
||||
Debug.LogWarning( "found touch Unity forgot to end with phase: " + _touchCache[i].phase );
|
||||
_touchCache[i].phase = TouchPhase.Ended;
|
||||
_liveTouches.Add( _touchCache[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void internalUpdateTouches()
|
||||
{
|
||||
// the next couple sections are disgustingly, appallingly, horrendously horrible due to all the #ifs but it
|
||||
// helps when testing in the editor
|
||||
#if UNITY_EDITOR
|
||||
// check to see if the Unity Remote is active
|
||||
if( shouldProcessMouseInput() )
|
||||
{
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_WEBPLAYER || UNITY_WEBGL
|
||||
|
||||
// we only need to process if we have some interesting input this frame
|
||||
if( Input.GetMouseButtonUp( 0 ) || Input.GetMouseButton( 0 ) )
|
||||
_liveTouches.Add( _touchCache[0].populateFromMouse() );
|
||||
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// get all touches and examine them. only do our touch processing if we have some touches
|
||||
if( Input.touchCount > 0 )
|
||||
{
|
||||
_shouldCheckForLostTouches = true;
|
||||
|
||||
var maxTouchIndexToExamine = Mathf.Min( Input.touches.Length, maxTouchesToProcess );
|
||||
for( var i = 0; i < maxTouchIndexToExamine; i++ )
|
||||
{
|
||||
var touch = Input.touches[i];
|
||||
if( touch.fingerId < maxTouchesToProcess )
|
||||
_liveTouches.Add( _touchCache[touch.fingerId].populateWithTouch( touch ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we guard this so that we only check once after all the touches are lifted
|
||||
if( _shouldCheckForLostTouches )
|
||||
{
|
||||
addTouchesUnityForgotToEndToLiveTouchesList();
|
||||
_shouldCheckForLostTouches = false;
|
||||
}
|
||||
}
|
||||
|
||||
// pass on the touches to all the recognizers
|
||||
if( _liveTouches.Count > 0 )
|
||||
{
|
||||
for( var i = 0; i < _gestureRecognizers.Count; i++ )
|
||||
_gestureRecognizers[i].recognizeTouches( _liveTouches );
|
||||
_liveTouches.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region MonoBehaviour
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// prep our TKTouch cache so we avoid excessive allocations
|
||||
_touchCache = new TKTouch[maxTouchesToProcess];
|
||||
for( int i = 0; i < maxTouchesToProcess; i++ )
|
||||
_touchCache[i] = new TKTouch( i );
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if( shouldAutoUpdateTouches )
|
||||
internalUpdateTouches();
|
||||
}
|
||||
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
_instance = null;
|
||||
Destroy( gameObject );
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// TouchKit will attempt to use the first camera to set this up. If that is not the camera you would like used
|
||||
/// just call this method with your HUD (or standard orthographic) camera.
|
||||
/// </summary>
|
||||
/// <param name="cam">Cam.</param>
|
||||
public static void setupPixelsToUnityUnitsMultiplierWithCamera( Camera cam )
|
||||
{
|
||||
if( !cam.orthographic )
|
||||
{
|
||||
Debug.LogError( "Attempting to setup unity pixel-to-units modifier with a non-orthographic camera" );
|
||||
return;
|
||||
}
|
||||
|
||||
var screenSizeUnityUnits = new Vector2( cam.aspect * cam.orthographicSize * 2f, cam.orthographicSize * 2f );
|
||||
_instance.pixelsToUnityUnitsMultiplier = new Vector2
|
||||
(
|
||||
screenSizeUnityUnits.x / (float)Screen.width,
|
||||
screenSizeUnityUnits.y / (float)Screen.height
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public static void updateTouches()
|
||||
{
|
||||
if( _instance == null )
|
||||
return;
|
||||
|
||||
_instance.internalUpdateTouches();
|
||||
}
|
||||
|
||||
|
||||
public static void addGestureRecognizer( TKAbstractGestureRecognizer recognizer )
|
||||
{
|
||||
// add, then sort and reverse so the higher zIndex items will be on top
|
||||
instance._gestureRecognizers.Add( recognizer );
|
||||
|
||||
if( recognizer.zIndex > 0 )
|
||||
{
|
||||
_instance._gestureRecognizers.Sort();
|
||||
_instance._gestureRecognizers.Reverse();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void removeGestureRecognizer( TKAbstractGestureRecognizer recognizer )
|
||||
{
|
||||
if( _instance == null )
|
||||
return;
|
||||
|
||||
if( !_instance._gestureRecognizers.Contains( recognizer ) )
|
||||
{
|
||||
Debug.LogError( "Trying to remove gesture recognizer that has not been added: " + recognizer );
|
||||
return;
|
||||
}
|
||||
|
||||
recognizer.reset();
|
||||
instance._gestureRecognizers.Remove( recognizer );
|
||||
}
|
||||
|
||||
|
||||
public static void removeAllGestureRecognizers()
|
||||
{
|
||||
if( _instance == null )
|
||||
return;
|
||||
|
||||
instance._gestureRecognizers.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e065ffc7dbca743ee9cef1b5106b0d26
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
Reference in new issue
Block a user