手势插件引入暂存

This commit is contained in:
Caiyanpeng committed 2021-10-08 16:03:55 +08:00
1 parent 6d1bc8e2c8
commit fd8f419f09
70 files changed
+5062 -126

No files matched your search

@@ -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: