using System; using System.Collections.Generic; using UnityEngine; public class ActionList : MonoBehaviour { // ----------------------- // // Interpolation Functions // // ----------------------- // public delegate float Interp(float start, float current, float end); public static float Linear(float start, float current, float end) { float dividend = end - start; if (Mathf.Approximately(dividend, 0f)) return 1.0f; float x = (current - start) / dividend; return Mathf.Clamp01(x); } public static float EaseInOut(float start, float current, float end) { float x = Linear(start, current, end); return x < 0.5 ? 4 * x * x * x : 1 - Mathf.Pow(-2 * x + 2, 3) / 2; } public static float EaseOut(float start, float current, float end) { float x = Linear(start, current, end); return 1 - Mathf.Pow(1 - x, 5); } // ----------------------- // // Actions // // ----------------------- // public abstract class BaseAction { private float timer = 0.0f; // Time alive private float delay; // Time before starting private readonly float interpTime; // Duration of Action private readonly Interp interpFunc = Linear; // Interpolation Function protected readonly GameObject target; // Object performing the Action protected enum ActionState { Waiting, Running } private ActionState state = ActionState.Waiting; protected BaseAction(GameObject _target, float _interpTime, float _delay = 0.0f) { target = _target; interpTime = Mathf.Max(0f, _interpTime); delay = _delay; } protected BaseAction(GameObject _target, float _interpTime, Interp _interpFunc, float _delay = 0.0f) { target = _target; interpTime = Mathf.Max(0f, _interpTime); interpFunc = _interpFunc ?? Linear; delay = _delay; } // Get a linear percent done between 0-1 public float GetPercentDone() { if (interpTime <= 0.0f) return 1.0f; return Linear(0, timer, interpTime); } // Get the result of the interpolation Function based on the actions timer protected float GetInterpolateMultiplier() { if (interpTime <= 0.0f) return 1.0f; return interpFunc(0.0f, timer, interpTime); } // Returns true if Interpolation has finished public bool Update() { // If the target GameObject has been deleted, delete this action if (target == null) return true; // Check for delay, and wait until done delay -= Time.deltaTime; if (delay > 0.0f) { return false; } else { // Transition from waiting to running exactly once if (state == ActionState.Waiting) { FirstFrameUpdate(); state = ActionState.Running; } // Update Timer timer += Time.deltaTime; if (timer > interpTime) timer = interpTime; // Perform the Action Act(); // If done, return true return timer >= interpTime; } } // Placeholder function for any first frame updates protected virtual void FirstFrameUpdate() { } // Abstract Function for action specific behavior protected abstract void Act(); } public class MoveAction : BaseAction { Vector3 start; Vector3 end; Vector3 diff; public MoveAction(GameObject _target, Vector2 _destination, float _interpTime, float _delay = 0) : base(_target, _interpTime, _delay) { end = _destination; } public MoveAction(GameObject _target, Vector2 _destination, float _interpTime, Interp _interpFunc, float _delay = 0) : base(_target, _interpTime, _interpFunc, _delay) { end = _destination; } protected override void Act() { target.transform.localPosition = start + (diff * GetInterpolateMultiplier()); } protected override void FirstFrameUpdate() { start = target.transform.localPosition; end.z = start.z; diff = end - start; } } public class ScaleAction : BaseAction { Vector3 start; Vector3 end; Vector3 diff; public ScaleAction(GameObject _target, Vector2 _end, float _interpTime, float _delay = 0) : base(_target, _interpTime, _delay) { end = _end; } public ScaleAction(GameObject _target, Vector2 _end, float _interpTime, Interp _interpFunc, float _delay = 0) : base(_target, _interpTime, _interpFunc, _delay) { end = _end; } protected override void Act() { target.transform.localScale = start + (diff * GetInterpolateMultiplier()); } protected override void FirstFrameUpdate() { start = target.transform.localScale; end.z = start.z; diff = end - start; } } public class CallbackAction : BaseAction { private Action fn; public CallbackAction(GameObject _target, Action _fn, float _delay) : base(_target, 0, _delay) { fn = _fn; } protected override void Act() { fn?.Invoke(); } } public class CanvasFadeAction : BaseAction { float start; float end; float diff; CanvasGroup canvas = null; public CanvasFadeAction(GameObject _target, float _end, float _interpTime, float _delay = 0) : base(_target, _interpTime, _delay) { end = _end; } public CanvasFadeAction(GameObject _target, float _end, float _interpTime, Interp _interpFunc, float _delay = 0) : base(_target, _interpTime, _interpFunc, _delay) { end = _end; } protected override void Act() { if (canvas != null) { canvas.alpha = start + (diff * GetInterpolateMultiplier()); } } protected override void FirstFrameUpdate() { canvas = target.GetComponent(); if (canvas != null) { start = canvas.alpha; diff = end - start; } } } // ----------------------- // // Action List // // ----------------------- // // The current list of actions being performed private readonly List currentActions = new List(); // Actions to add to the list at the beginning of every update private readonly List actionsToAdd = new List(); // Update is called once per frame void Update() { // Add new actions if (actionsToAdd.Count > 0) { currentActions.AddRange(actionsToAdd); actionsToAdd.Clear(); } // Update current actions // Note: Use in-place compacting to remove finished actions int write = 0; for (int read = 0; read < currentActions.Count; ++read) { BaseAction act = currentActions[read]; if (!act.Update()) { currentActions[write++] = act; } } // Remove the uncompacted range if (write < currentActions.Count) { currentActions.RemoveRange(write, currentActions.Count - write); } } public void Add(BaseAction act) { if (act == null) return; actionsToAdd.Add(act); } public void Clear() { currentActions.Clear(); actionsToAdd.Clear(); } }