From bf8ab46f936c97b341ee0f5a0f6a8a97b069e4c8 Mon Sep 17 00:00:00 2001 From: darthbator Date: Tue, 27 Jun 2017 12:58:00 -0700 Subject: [PATCH] Adding MF Swipe recognizer and Trigger zone recognizer --- .../Recognizers/TKMFSwipeRecognizer.cs | 204 ++++++++++++++++++ .../Recognizers/TKMFSwipeRecognizer.cs.meta | 12 ++ .../Recognizers/TKTriggerRecognizer.cs | 63 ++++++ .../Recognizers/TKTriggerRecognizer.cs.meta | 12 ++ 4 files changed, 291 insertions(+) create mode 100644 Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs create mode 100644 Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs.meta create mode 100644 Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs create mode 100644 Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs.meta diff --git a/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs b/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs new file mode 100644 index 0000000..0a31bec --- /dev/null +++ b/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs @@ -0,0 +1,204 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +/// +/// Class used to track finger swipes. Supports multiple fingers +/// +public class TKMFSwipe { + public List points = new List(); + public float startTime; + public float swipeVelocity; + public TKSwipeDirection direction; + + public Vector2 StartPoint { + get{ return points.FirstOrDefault(); } + } + + public Vector2 EndPoint { + get { return points.LastOrDefault(); } + } + + public Vector2 VectorDirection { + get { return (EndPoint - StartPoint).normalized; } + } +} + +public class TKMFSwipeRecognizer : TKAbstractGestureRecognizer { + /// + /// The event that fires when a swipe is recognized. + /// + public event System.Action gestureRecognizedEvent; + + /// + /// The maximum amount of time for the motion to be considered a swipe. + /// Setting to 0f will disable the time restriction completely. + /// + public float timeToSwipe = 0.5f; + + /// + /// The maximum number of simultaneous touches (fingers) on the screen to trigger + /// this swipe recognizer. Default is 2. + /// + public int maximumNumberOfTouches = 2; + + /// + /// 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. + /// + public bool triggerWhenCriteriaMet = true; + + + /// + /// 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. + /// + private float _minimumDistance = 2f; + + /// + /// A dictionary keyed on touches that represents the swipe data for each individual + /// finger one the screen + /// +// private List fingers = new List(); + private Dictionary fingers = new Dictionary(); + + public TKMFSwipeRecognizer() : this(2f) + { } + + public TKMFSwipeRecognizer(float minimumDistanceCm) + { + this._minimumDistance = minimumDistanceCm; + } + + + private bool CheckForSwipeCompletion(TKTouch touch) { + //Grab the swipe tracking we're dealing with + TKMFSwipe swipe = fingers[touch]; + + // if we have a time stipulation and we exceeded it stop listening for swipes, fail + if (timeToSwipe > 0.0f && (Time.time - swipe.startTime) > timeToSwipe) + return false; + + // if we don't have at least two points to test yet, then fail + if (this.fingers[touch].points.Count < 2) + return false; + + // the ideal distance in pixels from the start to the finish + float idealDistance = Vector2.Distance(swipe.StartPoint, swipe.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) + 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.fingers[touch].points.Count; i++) + realDistance += Vector2.Distance(this.fingers[touch].points[i], this.fingers[touch].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 + swipe.swipeVelocity = idealDistanceCM / (Time.time - swipe.startTime); + + // turn the slope of the ideal swipe line into an angle in degrees + Vector2 v2 = (swipe.EndPoint - swipe.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) + swipe.direction = TKSwipeDirection.UpRight; + else if (swipeAngle >= 247.5f && swipeAngle <= 292.5f) + swipe.direction = TKSwipeDirection.Up; + else if (swipeAngle >= 202.5f && swipeAngle <= 247.5f) + swipe.direction = TKSwipeDirection.UpLeft; + else if (swipeAngle >= 157.5f && swipeAngle <= 202.5f) + swipe.direction = TKSwipeDirection.Left; + else if (swipeAngle >= 112.5f && swipeAngle <= 157.5f) + swipe.direction = TKSwipeDirection.DownLeft; + else if (swipeAngle >= 67.5f && swipeAngle <= 112.5f) + swipe.direction = TKSwipeDirection.Down; + else if (swipeAngle >= 22.5f && swipeAngle <= 67.5f) + swipe.direction = TKSwipeDirection.DownRight; + else // swipeAngle >= 337.5f || swipeAngle <= 22.5f + swipe.direction = TKSwipeDirection.Right; + + return true; + } + + internal override void fireRecognizedEvent () {} + + //TODO call this manually + internal virtual void FireRecognizedEvent(TKTouch touch) { + //Remove tracking + _trackingTouches.Remove(touch); + TKMFSwipe swipingFinger = fingers[touch]; + fingers.Remove(touch); + + if (gestureRecognizedEvent != null) + gestureRecognizedEvent(swipingFinger); + + } + + internal override bool touchesBegan(List touches) { + foreach (var touch in touches) + if (fingers.Count < maximumNumberOfTouches) { + for (int i = 0; i < touches.Count; i++) { + if (!fingers.ContainsKey(touches[i])) { + this._trackingTouches.Add(touches[i]); //Add the touch to further tracking updates + Debug.Log("SWIPE Adding Touch " + touches[i].ToString()); + fingers.Add(touches[i], new TKMFSwipe()); //Add the touch to internal swipe tracking + fingers[touches[i]].points.Add(touches[i].position); //Update position + fingers[touches[i]].startTime = Time.time; //timestamp + } + } + state = TKGestureRecognizerState.Began; + return true; + } + return false; + } + + internal override void touchesMoved(List touches) { + for (int i = 0; i < touches.Count; i++) { + //Update points + fingers[touches[i]].points.Add(touches[i].position); + + // if we're triggering when the criteria is met, then check for completion every frame + if (triggerWhenCriteriaMet && CheckForSwipeCompletion(touches[i])) { + FireRecognizedEvent(touches[i]); + state = TKGestureRecognizerState.RecognizedAndStillRecognizing; + } + } + } + + internal override void touchesEnded(List touches) { + for (int i = 0; i < touches.Count; i++) { + if (!fingers.ContainsKey(touches[i])) + continue; + + //Update points + fingers[touches[i]].points.Add(touches[i].position); + + // last frame, one last check for recognition + if (CheckForSwipeCompletion(touches[i])) { + FireRecognizedEvent(touches[i]); + state = TKGestureRecognizerState.RecognizedAndStillRecognizing; + } + + Debug.Log("SWIPE Removing Touch " + touches[i].ToString()); + + //Remove from touch tracking + fingers.Remove(touches[i]); + _trackingTouches.Remove(touches[i]); + } + } +} diff --git a/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs.meta b/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs.meta new file mode 100644 index 0000000..2342f4e --- /dev/null +++ b/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fbce7135fc90b4a39af12251e5b1c1e7 +timeCreated: 1498593370 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs b/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs new file mode 100644 index 0000000..7aaaa1b --- /dev/null +++ b/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +/// +/// Tracks a single touch that starts within a given rect until that touch ends +/// +public class TKTriggerRecognizer : TKAbstractGestureRecognizer { + public event Action OnTriggerEvent; + public event Action OnTouchMovedEvent; + public event Action OnTouchEnd; + + public TKTouch touch { + get { return _trackingTouches[0]; } + } + + public TKTriggerRecognizer (TKRect _bounds) { + boundaryFrame = _bounds; + } + + #region implement TKAbstractGestureRecognizer + internal override void fireRecognizedEvent () {} + + internal override bool touchesBegan (List touches) { + if (_trackingTouches.Count == 0) { + for( int i = 0; i < touches.Count; i++ ) { + if (boundaryFrame.Value.contains(touches[i].position)) { + _trackingTouches.Add(touches[i]); + Debug.Log("TRIGGER Adding touchID " + touches[i].ToString()); + if (OnTriggerEvent != null) + OnTriggerEvent(touch); + + state = TKGestureRecognizerState.Began; + return true; + } + } + } + return false; + } + + internal override void touchesMoved (List touches) { + if (OnTouchMovedEvent != null) + OnTouchMovedEvent(touch); + + state = TKGestureRecognizerState.RecognizedAndStillRecognizing; + } + + internal override void touchesEnded (List touches) { + //FIXME I believe this is masking a bug lower in tocuhkit where a touch not applicable to this is coming in + for( int i = 0; i < touches.Count; i++ ) { +// if (touches[i] != touch) +// continue; + + Debug.Log("TRIGGER Ending touchID " + touches[i].ToString()); + if (OnTouchEnd != null) + OnTouchEnd(touch); + + state = TKGestureRecognizerState.FailedOrEnded; + } + } + #endregion +} diff --git a/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs.meta b/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs.meta new file mode 100644 index 0000000..3077f2a --- /dev/null +++ b/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0ced92d6ee7144a68b21d5a62267c8fa +timeCreated: 1498593360 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: