// ADD /// Appends a new ttTask. private TeaTime Add(float timeDelay, Func <float> timeDelayByFunc, Action callback, Action <ttHandler> callbackWithHandler) { // Ignores appends on Immutable mode if (!_isImmutable) { ttTask newTask = new ttTask(); newTask.time = timeDelay; newTask.timeByFunc = timeDelayByFunc; newTask.callback = callback; newTask.callbackWithHandler = callbackWithHandler; _tasks.Add(newTask); } // Autoplay if not paused or playing return(_isPaused || _isPlaying ? this : this.Play()); }
// LOOP /// Appends a callback loop (if duration is less than 0, the loop runs /// infinitely). private TeaTime Loop(float duration, Func <float> durationByFunc, Action <ttHandler> callback) { // Ignores appends on Immutable mode if (!_isImmutable) { ttTask newTask = new ttTask(); newTask.isLoop = true; newTask.time = duration; newTask.timeByFunc = durationByFunc; newTask.callbackWithHandler = callback; _tasks.Add(newTask); } // Autoplay if not paused or playing return(_isPaused || _isPlaying ? this : this.Play()); }
/// <summary> /// Appends a callback (timed or looped) into a queue. /// </summary> private static MonoBehaviour ttAdd(this MonoBehaviour instance, float timeDelay, YieldInstruction yieldDelay, Action callback, Action <ttHandler> callbackWithHandler, bool isLoop) { PrepareCurrentQueueName(instance); string queueName = currentQueueName[instance]; // Ignore locked if (IsLockedOrUnlockEmpty(instance, queueName)) { return(instance); } // Adds a new task in the main queue ttTask currentTask = new ttTask(instance, queueName, timeDelay, yieldDelay, callback, callbackWithHandler, isLoop); mainQueue[instance][queueName].Add(currentTask); // Mirrors the main queue in blueprints blueprints[instance][queueName].Add(currentTask); // Execute when isn't paused if (!IsPaused(instance, queueName)) { instance.StartCoroutine(ExecuteQueue(instance, queueName)); } return(instance); }
// THE COROUTINE /// This is the main algorithm. Executes all tasks, one after the /// other, calling their callbacks according to type, time and queue /// config. private IEnumerator ExecuteQueue() { _isPlaying = true; int reverseLastTask = -1; // Important: This value needs to be reset to default on most queue changes _lastPlayExecutedCount = 0; // :D! // Let's wait // 1 For secuencial Adds or Loops before their first execution // 2 Maybe a callback is trying to modify his own queue yield return(ttYield.EndOfFrame); while (_currentTask < _tasks.Count) { // Current task to be executed int taskId = _currentTask; if (_isReversed) { taskId = _tasks.Count - 1 - _currentTask; } ttTask currentTask = _tasks[taskId]; // Next task (or previous if the queue is backward) _currentTask++; // Avoid executing a task twice when reversed and the queue // hasn't reached the end if (taskId == reverseLastTask) { continue; } reverseLastTask = taskId; // :D? // yield return ttYield.EndOfFrame; // It's a loop if (currentTask.isLoop) { // Holds the duration float loopDuration = currentTask.time; // Func<float> added if (currentTask.timeByFunc != null) { loopDuration += currentTask.timeByFunc(); } // Nothing to do, skip if (loopDuration == 0) { continue; } // Loops will always need a handler ttHandler loopHandler = new ttHandler(); loopHandler.self = this; loopHandler.isLooping = true; loopHandler.isReversed = _isReversed; // Negative time means the loop is infinite bool isInfinite = loopDuration < 0; // T quotient float tRate = isInfinite ? 0 : 1 / loopDuration; // Progresion depends on current direction if (loopHandler.isReversed) { loopHandler.t = 1f; tRate = -tRate; } // While looping and, until time or infinite while (loopHandler.isLooping && (loopHandler.isReversed ? loopHandler.t >= 0 : loopHandler.t <= 1)) { // Check for queue reversal if (_isReversed != loopHandler.isReversed) { tRate = -tRate; loopHandler.isReversed = _isReversed; } float unityDeltaTime = Time.deltaTime; // Completion % from 0 to 1 if (!isInfinite) { loopHandler.t += tRate * unityDeltaTime; } // On finite loops this .deltaTime is sincronized with // the exact loop duration loopHandler.deltaTime = isInfinite ? unityDeltaTime : 1 / (loopDuration - loopHandler.timeSinceStart) * unityDeltaTime; // .deltaTime is also reversed if (loopHandler.isReversed) { loopHandler.deltaTime = -loopHandler.deltaTime; } // A classic loopHandler.timeSinceStart += unityDeltaTime; // Pause? while (_isPaused) { yield return(null); } // Loops will always have a callback with a handler currentTask.callbackWithHandler(loopHandler); // Handler .WaitFor( if (loopHandler.yieldsToWait != null) { for (int i = 0, len = loopHandler.yieldsToWait.Count; i < len; i++) { yield return(loopHandler.yieldsToWait[i]); } loopHandler.yieldsToWait.Clear(); } // Minimum sane delay if (loopHandler.yieldsToWait == null) { yield return(null); } } // Executed +1 _executedCount += 1; _lastPlayExecutedCount += 1; } // It's a timed callback else { // Holds the delay float delayDuration = currentTask.time; // Func<float> added if (currentTask.timeByFunc != null) { delayDuration += currentTask.timeByFunc(); } // // Time delay // if (delayDuration > 0) // yield return ttYield.Seconds(delayDuration); // Is this more precise that the previous commented code? float time = 0; while (time < delayDuration) { time += Time.deltaTime; yield return(null); } // Pause? while (_isPaused) { yield return(null); } // Normal callback if (currentTask.callback != null) { currentTask.callback(); } // Callback with handler if (currentTask.callbackWithHandler != null) { ttHandler handler = new ttHandler(); handler.self = this; handler.t = 1; handler.timeSinceStart = delayDuration; handler.deltaTime = Time.deltaTime; currentTask.callbackWithHandler(handler); // Handler WaitFor if (handler.yieldsToWait != null) { for (int i = 0, len = handler.yieldsToWait.Count; i < len; i++) { yield return(handler.yieldsToWait[i]); } handler.yieldsToWait.Clear(); } // Minimum sane delay if (delayDuration <= 0 && handler.yieldsToWait == null) { yield return(null); } } else if (delayDuration <= 0) { yield return(null); } // Executed +1 _executedCount += 1; _lastPlayExecutedCount += 1; } // Just at the end of a complete queue execution if (_tasks.Count > 0 && _currentTask >= _tasks.Count) { // Forget current nested queues _waiting.Clear(); } // Consume mode removes the task after execution // #todo Need to be tested with .Reverse() stuff if (_isConsuming) { _currentTask -= 1; _tasks.Remove(currentTask); reverseLastTask = -1; // To default } // On Yoyo mode the queue is reversed at the end, only once per // play without Repeat mode if (_isYoyo && _currentTask >= _tasks.Count && (_lastPlayExecutedCount <= _tasks.Count || _isRepeating)) { this.Reverse(); reverseLastTask = -1; // To default } // Repeats on Repeat mode if (_isRepeating && _tasks.Count > 0 && _currentTask >= _tasks.Count) { _currentTask = 0; reverseLastTask = -1; // To default } } // Done! _isPlaying = false; yield return(null); }