Files
Main/Assets/Launcher/ExternalLibs/FingerGestures/Components/Base/GestureRecognizer.cs
2025-01-25 04:38:09 +08:00

336 lines
9.4 KiB
C#

using UnityEngine;
/// <summary>
/// The base class for all gesture recognizers
/// </summary>
public abstract class GestureRecognizer : FGComponent
{
/// <summary>
/// Possible gesture states
/// </summary>
public enum GestureState
{
/// <summary>
/// The gesture recognizer is ready and waiting for the correct initial input conditions to begin
/// </summary>
Ready,
/// <summary>
/// The gesture recognition has started and is still going on
/// </summary>
InProgress,
/// <summary>
/// The gesture detected a user input that invalidated it
/// </summary>
Failed,
/// <summary>
/// The gesture was succesfully recognized
/// </summary>
Recognized,
}
/// <summary>
/// The reset mode determines when to reset a GestureRecognizer after it fails or succeed (GestureState.Failed or GestureState.Recognized)
/// </summary>
public enum GestureResetMode
{
/// <summary>
/// The gesture recognizer will reset on the next Update()
/// </summary>
NextFrame,
/// <summary>
/// The gesture recognizer will reset at the end of the current multitouch sequence
/// </summary>
EndOfTouchSequence,
/// <summary>
/// The gesture recognizer will reset at the beginning of the next multitouch sequence
/// </summary>
StartOfTouchSequence,
}
#region State
/// <summary>
/// Event fired whenever the gesture recognizer state changes
/// See also: <see cref="State"/> and <see cref="PreviousState"/>
/// </summary>
public event EventDelegate<GestureRecognizer> OnStateChanged;
GestureState prevState = GestureState.Ready;
GestureState state = GestureState.Ready;
float startTime = 0;
/// <summary>
/// Get the previous gesture state
/// </summary>
public GestureState PreviousState
{
get { return prevState; }
}
/// <summary>
/// Get or set the current gesture state
/// </summary>
public GestureState State
{
get { return state; }
protected set
{
if( state != value )
{
prevState = state;
state = value;
if( OnStateChanged != null )
OnStateChanged( this );
}
}
}
/// <summary>
/// Whether or not the gesture recognition is in on-going
/// See also: <see cref="State"/>
/// </summary>
public bool IsActive
{
get { return State == GestureState.InProgress; }
}
/// <summary>
/// Time at which the gesture recognition started (when OnBegin() is called)
/// </summary>
public float StartTime
{
get { return startTime; }
protected set { startTime = value; }
}
/// <summary>
/// Amount of time elapsed since the gesture recognition started (in seconds)
/// </summary>
public float ElapsedTime
{
get { return Time.time - startTime; }
}
#endregion
#region Reset
/// <summary>
/// Get or set the reset mode for this gesture recognizer
/// </summary>
public GestureResetMode ResetMode = GestureResetMode.StartOfTouchSequence;
/// <summary>
/// Put back the gesture recognizer in Ready state and reset any relevant data
/// See also: <see cref="ResetMode"/>
/// </summary>
protected virtual void Reset()
{
State = GestureState.Ready;
}
#endregion
protected override void Start()
{
base.Start();
Reset();
}
#region Touch Sequence
/// <summary>
/// Called when the first finger of a new multi-touch sequence has touched the screen
/// </summary>
protected virtual void OnTouchSequenceStarted()
{
if( ResetMode == GestureResetMode.StartOfTouchSequence )
{
if( State == GestureState.Recognized || State == GestureState.Failed )
Reset();
}
}
/// <summary>
/// Called when all the fingers that participated in the current multi-touch sequence are no longer touching the screen
/// </summary>
protected virtual void OnTouchSequenceEnded()
{
if( ResetMode == GestureResetMode.EndOfTouchSequence )
{
if( State == GestureState.Recognized || State == GestureState.Failed )
Reset();
}
}
#endregion
int lastTouchesCount = 0;
protected override void OnUpdate( FingerGestures.IFingerList touches )
{
if( touchFilter != null )
touches = touchFilter.Apply( touches );
if( touches.Count > 0 && lastTouchesCount == 0 )
OnTouchSequenceStarted();
switch( State )
{
case GestureState.Recognized:
case GestureState.Failed:
if( ResetMode == GestureResetMode.NextFrame )
Reset();
break;
case GestureState.Ready:
State = OnReady( touches );
break;
case GestureState.InProgress:
State = OnActive( touches );
break;
default:
UnityEngine.Debug.LogError( this + " - Unhandled state: " + State + ". Failing recognizer." );
State = GestureState.Failed;
break;
}
if( touches.Count == 0 && lastTouchesCount > 0 )
OnTouchSequenceEnded();
lastTouchesCount = touches.Count;
}
protected virtual GestureState OnReady( FingerGestures.IFingerList touches )
{
if( ShouldFailFromReady( touches ) )
return GestureState.Failed;
if( CanBegin( touches ) )
{
StartTime = Time.time;
OnBegin( touches );
return GestureState.InProgress;
}
return GestureState.Ready;
}
protected virtual bool ShouldFailFromReady( FingerGestures.IFingerList touches )
{
if( touches.Count != GetRequiredFingerCount() )
{
if( touches.Count > 0 && !Young( touches ) )
return true;
}
return false;
}
/// <summary>
/// This controls whether or not the gesture recognition should begin
/// </summary>
/// <param name="touches">The active touches</param>
protected virtual bool CanBegin( FingerGestures.IFingerList touches )
{
if( touches.Count != GetRequiredFingerCount() )
return false;
// check with the delegate (provided we have one set)
if( !CheckCanBeginDelegate( touches ) )
return false;
return true;
}
public virtual bool CheckCanBeginDelegate( FingerGestures.IFingerList touches )
{
if( canBeginDelegate != null && !canBeginDelegate( this, touches ) )
return false;
return true;
}
#region CanBegin Delegate
/// <summary>
/// Delegate used to control if a gesture recognizer is allowed to begin the gesture detection
/// </summary>
/// <param name="gr">The gesture recognizer to handle</param>
/// <param name="touches">A list of all the touches for this frame</param>
/// <returns></returns>
public delegate bool CanBeginDelegate( GestureRecognizer gr, FingerGestures.IFingerList touches );
CanBeginDelegate canBeginDelegate;
public void SetCanBeginDelegate( CanBeginDelegate f ) { canBeginDelegate = f; }
public CanBeginDelegate GetCanBeginDelegate() { return canBeginDelegate; }
#endregion
#region Abstract methods to implement in derived classes
/// <summary>
/// Return the exact number of active touches required for the gesture to be valid
/// </summary>
protected abstract int GetRequiredFingerCount();
/// <summary>
/// Method called when the gesture recognizer has just started recognizing a valid gesture
/// </summary>
/// <param name="touches">The active touches</param>
protected abstract void OnBegin( FingerGestures.IFingerList touches );
/// <summary>
/// Method called on each frame that the gesture recognizer is in an active state
/// </summary>
/// <param name="touches">The active touches</param>
/// <returns>The new state the gesture recognizer should be in</returns>
protected abstract GestureState OnActive( FingerGestures.IFingerList touches );
#endregion
#region Touch Filter
FingerGestures.ITouchFilter touchFilter = null;
/// <summary>
/// Get or set the touch filter used to modify the list of active touches about to be processed by the gesture recognizer
/// </summary>
public FingerGestures.ITouchFilter TouchFilter
{
get { return touchFilter; }
set { touchFilter = value; }
}
#endregion
#region Utils
/// <summary>
/// Check if all the touches in the list started recently
/// </summary>
/// <param name="touches">The touches to evaluate</param>
/// <returns>True if the age of each touch in the list is under a set threshold</returns>
protected bool Young( FingerGestures.IFingerList touches )
{
FingerGestures.Finger oldestTouch = touches.GetOldest();
if( oldestTouch == null )
return false;
float elapsedTimeSinceFirstTouch = Time.time - oldestTouch.StarTime;
return elapsedTimeSinceFirstTouch < 0.25f;
}
#endregion
}