216 lines
5.6 KiB
C#
216 lines
5.6 KiB
C#
using System;
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// Reusable action execution system with busy state management.
|
|
/// Replaces repeated coroutine busy-state patterns across the codebase.
|
|
/// </summary>
|
|
public class ActionExecutor : MonoBehaviour
|
|
{
|
|
public bool IsBusy { get; private set; }
|
|
|
|
private Coroutine _currentAction;
|
|
|
|
/// <summary>
|
|
/// Event fired when an action starts.
|
|
/// </summary>
|
|
public event Action OnActionStarted;
|
|
|
|
/// <summary>
|
|
/// Event fired when an action completes.
|
|
/// </summary>
|
|
public event Action OnActionCompleted;
|
|
|
|
/// <summary>
|
|
/// Event fired when an action is cancelled.
|
|
/// </summary>
|
|
public event Action OnActionCancelled;
|
|
|
|
/// <summary>
|
|
/// Try to execute an action. Returns false if already busy.
|
|
/// </summary>
|
|
/// <param name="request">The action request to execute</param>
|
|
/// <returns>True if action started, false if busy</returns>
|
|
public bool TryExecute(ActionRequest request)
|
|
{
|
|
if (IsBusy) return false;
|
|
|
|
_currentAction = StartCoroutine(ExecuteRoutine(request));
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cancel the current action if one is running.
|
|
/// </summary>
|
|
public void Cancel()
|
|
{
|
|
if (_currentAction != null)
|
|
{
|
|
StopCoroutine(_currentAction);
|
|
_currentAction = null;
|
|
IsBusy = false;
|
|
OnActionCancelled?.Invoke();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Force reset the busy state. Use with caution.
|
|
/// </summary>
|
|
public void ForceReset()
|
|
{
|
|
if (_currentAction != null)
|
|
{
|
|
StopCoroutine(_currentAction);
|
|
_currentAction = null;
|
|
}
|
|
IsBusy = false;
|
|
}
|
|
|
|
private IEnumerator ExecuteRoutine(ActionRequest request)
|
|
{
|
|
IsBusy = true;
|
|
OnActionStarted?.Invoke();
|
|
|
|
// Pre-action callback
|
|
request.OnStart?.Invoke();
|
|
|
|
// Animation trigger
|
|
if (request.Animator != null && !string.IsNullOrEmpty(request.AnimTrigger))
|
|
{
|
|
if (request.AnimSpeed > 0)
|
|
{
|
|
request.Animator.SetFloat("ActionSpeed", request.AnimSpeed);
|
|
}
|
|
request.Animator.SetTrigger(request.AnimTrigger);
|
|
}
|
|
|
|
// Wait for impact point
|
|
if (request.ImpactDelay > 0)
|
|
{
|
|
float adjustedDelay = request.AnimSpeed > 0
|
|
? request.ImpactDelay / request.AnimSpeed
|
|
: request.ImpactDelay;
|
|
yield return new WaitForSeconds(adjustedDelay);
|
|
}
|
|
|
|
// Execute main effect
|
|
request.OnImpact?.Invoke();
|
|
|
|
// Wait for remaining duration
|
|
float remainingTime = request.TotalDuration - request.ImpactDelay;
|
|
if (request.AnimSpeed > 0)
|
|
{
|
|
remainingTime /= request.AnimSpeed;
|
|
}
|
|
|
|
if (remainingTime > 0)
|
|
{
|
|
yield return new WaitForSeconds(remainingTime);
|
|
}
|
|
|
|
// Completion callback
|
|
request.OnComplete?.Invoke();
|
|
|
|
IsBusy = false;
|
|
_currentAction = null;
|
|
OnActionCompleted?.Invoke();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configuration for an action to be executed by ActionExecutor.
|
|
/// </summary>
|
|
[Serializable]
|
|
public struct ActionRequest
|
|
{
|
|
/// <summary>
|
|
/// Animator to trigger animations on.
|
|
/// </summary>
|
|
public Animator Animator;
|
|
|
|
/// <summary>
|
|
/// Animation trigger name.
|
|
/// </summary>
|
|
public string AnimTrigger;
|
|
|
|
/// <summary>
|
|
/// Animation playback speed multiplier.
|
|
/// </summary>
|
|
public float AnimSpeed;
|
|
|
|
/// <summary>
|
|
/// Time delay before the impact/effect happens (for syncing with animation).
|
|
/// </summary>
|
|
public float ImpactDelay;
|
|
|
|
/// <summary>
|
|
/// Total duration of the action.
|
|
/// </summary>
|
|
public float TotalDuration;
|
|
|
|
/// <summary>
|
|
/// Callback invoked when action starts.
|
|
/// </summary>
|
|
public Action OnStart;
|
|
|
|
/// <summary>
|
|
/// Callback invoked at the impact moment.
|
|
/// </summary>
|
|
public Action OnImpact;
|
|
|
|
/// <summary>
|
|
/// Callback invoked when action completes.
|
|
/// </summary>
|
|
public Action OnComplete;
|
|
|
|
/// <summary>
|
|
/// Create a simple action request with just timing.
|
|
/// </summary>
|
|
public static ActionRequest Simple(float duration, Action onComplete)
|
|
{
|
|
return new ActionRequest
|
|
{
|
|
TotalDuration = duration,
|
|
AnimSpeed = 1f,
|
|
OnComplete = onComplete
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an animated action request.
|
|
/// </summary>
|
|
public static ActionRequest Animated(Animator animator, string trigger, float duration,
|
|
float impactDelay, Action onImpact, float speed = 1f)
|
|
{
|
|
return new ActionRequest
|
|
{
|
|
Animator = animator,
|
|
AnimTrigger = trigger,
|
|
AnimSpeed = speed,
|
|
ImpactDelay = impactDelay,
|
|
TotalDuration = duration,
|
|
OnImpact = onImpact
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a full action request with all callbacks.
|
|
/// </summary>
|
|
public static ActionRequest Full(Animator animator, string trigger, float duration,
|
|
float impactDelay, float speed, Action onStart, Action onImpact, Action onComplete)
|
|
{
|
|
return new ActionRequest
|
|
{
|
|
Animator = animator,
|
|
AnimTrigger = trigger,
|
|
AnimSpeed = speed,
|
|
ImpactDelay = impactDelay,
|
|
TotalDuration = duration,
|
|
OnStart = onStart,
|
|
OnImpact = onImpact,
|
|
OnComplete = onComplete
|
|
};
|
|
}
|
|
}
|