Hi,
Ok, don't have time to finish this for today, but try this action. It's dealing with swipe detection, and maintain a value representing the direction and another representing the animation value ( randing from 0 to 1, proportionnal to the screen width or height depending on the swipe direction).
Now, that I did all the ground work, I realize that an fsm would have been better, because I can't leave this action or else I loose track of the swipe, so I need to implement some cumbersome events sending to another fsm, not big deal, but did not plan on that on my allocated time...
So, yes, int this case, an fsm could makes more sense, because values about the swipe woul dbe maintained and I would evolved within a single fsm between states to then trigger external events as required. But I bet the next versions of playmaker will provide much better UI selectors for events and such and will make this trivial as if I was sending events to the same fsm.
I welcome comments and suggestions on this code, as there are many ways to deal with such behavior and anyway far from complete.
-- need to expose a lot more settings to tweak the behavior without touching the code.
-- direction changes not taken in consideration
-- an absolute vector representing the movement would maybe be useful, or at east easier to handle by other fsm to know what to do
and this doesn't deal with the actual visuals you want to tight to the swipe event, and that is a major part of the work. dealing with knowing where you are, where to go, and all that. But that's already a different aspect of your problem
tested on the device, and it's all smooth,
// (c) Copyright HutongGames, LLC 2010-2011. All rights reserved.
using UnityEngine;
namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.Device)]
[Tooltip("Control and maintain infos about swipe gestures live.")]
public class SwipeLiveGestureEvent : FsmStateAction
{
[Tooltip("How far a touch has to travel to be trigger a swipe action (in pixels)")]
public FsmInt minSwipeTriggerDistance;
[Tooltip("How far a touch has to travel to be considered a valid swipe (e.g. 1 = 1 screen distance on the swipe axis)")]
public FsmFloat minTravelTriggerPercent;
private float minTravelTriggerPixels;
[Tooltip("How fast a touch has to drag to be considered a valid swipe in pixels per seconds")]
public FsmFloat minSpeedTrigger;
[Tooltip("How fast do we animate the swipe when gesture ends")]
public FsmFloat animationSpeed = 10;
[UIHint(UIHint.Variable)]
[Tooltip("Store the swipe direction: none | left | right | up | down")]
public FsmString swipeDirection;
[UIHint(UIHint.Variable)]
[Tooltip("Store the swipe value ranging from 0 to 1")]
public FsmFloat swipeValue;
private bool touchStarted;// has a one-finger-only touch started
private bool swipeStarted; // has the gesture been accepted as a swipe.
private bool swipeCanTrigger; // the swipe gesture is now effective. If true, when gestures end, the swipe will automatically finish
private Vector2 touchStartPos;
private Vector2 swipeDrag;
private enum Direction {none,left,right,up,down};
private Direction _swipeDirection = Direction.none;
private Touch _touch_;
private float animeTarget;
private bool isAnimating;
//float touchStartTime;
public override void Reset()
{
minSwipeTriggerDistance = 2;
minTravelTriggerPercent = 0.5f; // half the screen width or height depending on the swipe direction.
minSpeedTrigger = 100;// in pixels per seconds.
_swipeDirection = Direction.none;
swipeValue = null;
swipeDirection = null;
resetSwipeData();
}
public override void OnEnter()
{
resetSwipeData();
}
public override void OnUpdate()
{
if (isAnimating){
updateTransitionAnimation();
}
// we only deal with one finger touch
if (Input.touchCount != 1)
{
return;
}
_touch_ = Input.touches[0];
switch (_touch_.phase)
{
case TouchPhase.Began:
touchStarted = true;
touchStartPos = _touch_.position;
resetSwipeData();
//touchStartTime = Time.realtimeSinceStartup;
break;
case TouchPhase.Ended:
case TouchPhase.Canceled:
if (touchStarted)
{
// check velocity trigger is distance trigger did not raise
if (!swipeCanTrigger){
float _dragReleaseDeltaMagnitude = _touch_.deltaPosition.magnitude ;
float _dragReleaseDeltaTime = _touch_.deltaTime ;
float _dragReleasespeed = _dragReleaseDeltaMagnitude*_dragReleaseDeltaTime;
Debug.Log("The user swipe released at speed"+_dragReleasespeed);
if (_dragReleasespeed>minSpeedTrigger.Value){
Debug.Log("we accept the swipe speed wise");
swipeCanTrigger= true;
}
}
triggerTransitionAnimation();
touchStarted = false;
}
break;
case TouchPhase.Moved:
// compute the drag vector
swipeDrag = _touch_.position - touchStartPos ;
// detect when the gesture is considered a swipe
if (!swipeStarted){
// check the direction and validate the minimal distance to initiate ( not necessarly validate) a swipe gesture
if ( Mathf.Abs(swipeDrag.x) > Mathf.Abs(swipeDrag.y) && Mathf.Abs(swipeDrag.x) >= minSwipeTriggerDistance.Value ){
swipeStarted = true;
// we go horizontaly, yes, but in what direction
_swipeDirection = swipeDrag.x<0 ? Direction.right:Direction.left;
swipeDirection.Value = _swipeDirection.ToString();
minTravelTriggerPixels = Screen.width*minTravelTriggerPercent.Value;
}else if ( Mathf.Abs(swipeDrag.x) < Mathf.Abs(swipeDrag.y) && Mathf.Abs(swipeDrag.y)>=minSwipeTriggerDistance.Value ){
swipeStarted = true;
// we go verticaly, yes, but in what direction
_swipeDirection = swipeDrag.y<0 ? Direction.up:Direction.down;
swipeDirection.Value = _swipeDirection.ToString();
minTravelTriggerPixels = Screen.height*minTravelTriggerPercent.Value;
}
}
// !swipeStarted and swipeStart check are not tight in with an "else" so that the swipeValue is straight away computed and avoid latency.
if (swipeStarted){
// let's update the swipe value
if (_swipeDirection == Direction.right || _swipeDirection == Direction.left){
swipeValue.Value = Mathf.Abs(swipeDrag.x)/Screen.width;
if (!swipeCanTrigger){
if ( Mathf.Abs(swipeDrag.x) > minTravelTriggerPixels ){
swipeCanTrigger = true;
}
}
}else if ( _swipeDirection == Direction.up || _swipeDirection == Direction.down) {
swipeValue.Value = Mathf.Abs(swipeDrag.y)/Screen.height;
if (!swipeCanTrigger){
if ( Mathf.Abs(swipeDrag.y) > minTravelTriggerPixels ){
swipeCanTrigger = true;
}
}
}
// also we check user input for clues like drag distance
// and decide on the gesture validity.
}
break;
}
}
private void triggerTransitionAnimation(){
if (swipeCanTrigger){
animeTarget = 1f;
isAnimating = true;
}else{
// we animate back
animeTarget = 0f;
isAnimating = true;
}
}// animateTransition
private void updateTransitionAnimation(){
// TODO: make it with iTween or EZGUI tweening system for more flexibilities.
swipeValue.Value = Mathf.Lerp(swipeValue.Value,animeTarget,animationSpeed.Value*Time.deltaTime);
// check if we have finished our animation
// check can be improved, but will do for now
if ( Mathf.Abs(swipeValue.Value-animeTarget)<0.001 ){
swipeValue.Value = animeTarget;
// WE ARE DONE WITH MOVING
isAnimating = false;
_swipeDirection = Direction.none;
resetSwipeData();
}// we have finished animating
}// _updateTransitionAnimation
private void resetSwipeData(){
if (swipeDirection != null){
swipeDirection.Value = _swipeDirection.ToString();
}
swipeCanTrigger = false;
swipeStarted = false;
swipeDrag = Vector2.zero;
if (swipeValue != null){
swipeValue.Value = 0f;
}
}
/*
void TestForSwipeGesture(Touch touch)
{
// test min distance
var lastPos = touch.position;
var distance = Vector2.Distance(lastPos, touchStartPos);
if (distance > minSwipeDistancePixels)
{
float dy = lastPos.y - touchStartPos.y;
float dx = lastPos.x - touchStartPos.x;
float angle = Mathf.Rad2Deg * Mathf.Atan2(dx, dy);
angle = (360 + angle - 45) % 360;
Debug.Log (angle);
if (angle < 90)
{
Fsm.Event(swipeRightEvent);
}
else if (angle < 180)
{
Fsm.Event(swipeDownEvent);
}
else if (angle < 270)
{
Fsm.Event(swipeLeftEvent);
}
else
{
Fsm.Event(swipeUpEvent);
}
}
}
*/
}
}
Bye,
Jean