// @ // THE COROUTINE /// <summary> /// This is the main algorithm. Executes all tasks, one after the /// other, calling their callbacks according to type, time and queue /// config. /// </summary> private IEnumerator ExecuteQueue() { _isPlaying = true; int reverseLastTask = -1; // Important: This value needs to be reset to default on most queue changes _lastPlayExecutedCount = 0; 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; // 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); // 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)); } // 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; } // 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 } // Just at the end of a complete queue execution if (_tasks.Count > 0 && _currentTask >= _tasks.Count) { // A new cycle begins _waiting.Clear(); } } // Done! _isPlaying = false; yield return(null); }
// ^ // THE COROUTINE /// <summary> /// This is the main algorithm. Executes all tasks, one after the /// other, calling their callbacks according to type, time and queue /// config. /// </summary> IEnumerator ExecuteQueue() { _isPlaying = true; while (_currentTask < _tasks.Count) { // Current ttTask currentTask = _tasks[_currentTask]; // Next _currentTask += 1; // 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 new WaitForEndOfFrame(); // It's a loop if (currentTask.isLoop) { // Nothing to do, skip if (currentTask.time == 0) continue; // Loops always need a handler ttHandler loopHandler = new ttHandler(); loopHandler.self = this; // Negative time means the loop is infinite bool isInfinite = currentTask.time < 0; // T quotient float tRate = isInfinite ? 0 : 1 / currentTask.time; // While active and, until time or infinite while (loopHandler.isActive && loopHandler.t <= 1) { 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 / (currentTask.time - loopHandler.timeSinceStart) * unityDeltaTime; loopHandler.timeSinceStart += unityDeltaTime; // Pause? while (_isPaused) yield return null; // Loops always have a callback with a handler currentTask.callbackWithHandler(loopHandler); // Handler .WaitFor( if (loopHandler.yieldsToWait != null) { foreach (YieldInstruction yi in loopHandler.yieldsToWait) yield return yi; loopHandler.yieldsToWait.Clear(); } // Minimum sane delay if (loopHandler.yieldsToWait == null) yield return null; } } // It's a timed callback else { // Time delay if (currentTask.time > 0) yield return new WaitForSeconds(currentTask.time); // Yield delay if (currentTask.yieldInstruction != null) yield return currentTask.yieldInstruction; // Pause? while (_isPaused) yield return null; // Normal callback if (currentTask.callback != null) currentTask.callback(); // Callback with handler ttHandler handler = new ttHandler(); handler.self = this; if (currentTask.callbackWithHandler != null) { handler.isActive = true; handler.t = 1; handler.timeSinceStart = currentTask.time; handler.deltaTime = Time.deltaTime; currentTask.callbackWithHandler(handler); // Handler WaitFor if (handler.yieldsToWait != null) { foreach (YieldInstruction yi in handler.yieldsToWait) yield return yi; handler.yieldsToWait.Clear(); } } // Minimum sane delay if (currentTask.time <= 0 && handler.yieldsToWait == null) yield return null; } // Consume mode removes the task after execution if (_isConsuming) { _currentTask -= 1; _tasks.Remove(currentTask); } } // Repeats on Repeat mode (if needed) if (_isRepeating && _tasks.Count > 0) { _currentTask = 0; _currentCoroutine = _instance.StartCoroutine(ExecuteQueue()); } // Done! else { _isPlaying = false; } yield return null; }