using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace Coroutine { /// /// An object of this class can be used to start, tick and otherwise manage active s as well as their s. /// Note that a static implementation of this can be found in . /// public class CoroutineHandlerInstance { private readonly List tickingCoroutines = new List(); private readonly List eventCoroutines = new List(); private readonly Queue outstandingCoroutines = new Queue(); private readonly ISet eventCoroutinesToRemove = new HashSet(); private readonly Stopwatch stopwatch = new Stopwatch(); /// /// The amount of instances that are currently waiting for a tick (waiting for time to pass) /// public int TickingCount => this.tickingCoroutines.Count; /// /// The amount of instances that are currently waiting for an /// public int EventCount => this.eventCoroutines.Count; /// /// Starts the given coroutine, returning a object for management. /// Note that this calls to get the enumerator. /// /// The coroutine to start /// The that this coroutine should have. Defaults to an empty string. /// The that this coroutine should have. The higher the priority, the earlier it is advanced. Defaults to 0. /// An active coroutine object representing this coroutine public ActiveCoroutine Start(IEnumerable coroutine, string name = "", int priority = 0) { return this.Start(coroutine.GetEnumerator(), name, priority); } /// /// Starts the given coroutine, returning a object for management. /// /// The coroutine to start /// The that this coroutine should have. Defaults to an empty string. /// The that this coroutine should have. The higher the priority, the earlier it is advanced compared to other coroutines that advance around the same time. Defaults to 0. /// An active coroutine object representing this coroutine public ActiveCoroutine Start(IEnumerator coroutine, string name = "", int priority = 0) { var inst = new ActiveCoroutine(coroutine, name, priority, this.stopwatch); if (inst.MoveNext()) this.outstandingCoroutines.Enqueue(inst); return inst; } /// /// Causes the given action to be invoked after the given . /// This is equivalent to a coroutine that waits for the given wait and then executes the given . /// /// The wait to wait for /// The action to execute after waiting /// The that the underlying coroutine should have. Defaults to an empty string. /// The that the underlying coroutine should have. The higher the priority, the earlier it is advanced compared to other coroutines that advance around the same time. Defaults to 0. /// An active coroutine object representing this coroutine public ActiveCoroutine InvokeLater(Wait wait, Action action, string name = "", int priority = 0) { return this.Start(InvokeLaterImpl(wait, action), name, priority); } /// /// Ticks this coroutine handler, causing all time-based s to be ticked. /// Note that this method needs to be called even if only event-based coroutines are used. /// /// The amount of seconds that have passed since the last time this method was invoked public void Tick(double deltaSeconds) { this.MoveOutstandingCoroutines(); this.tickingCoroutines.RemoveAll(c => { if (c.Tick(deltaSeconds)) { return true; } else if (c.IsWaitingForEvent()) { this.outstandingCoroutines.Enqueue(c); return true; } return false; }); } /// /// Ticks this coroutine handler, causing all time-based s to be ticked. /// This is a convenience method that calls , but accepts a instead of an amount of seconds. /// /// The time that has passed since the last time this method was invoked public void Tick(TimeSpan delta) { this.Tick(delta.TotalSeconds); } /// /// Raises the given event, causing all event-based s to be updated. /// /// The event to raise public void RaiseEvent(Event evt) { for (var i = 0; i < this.eventCoroutines.Count; i++) { if (this.eventCoroutinesToRemove.Contains(i)) continue; var c = this.eventCoroutines[i]; if (c.OnEvent(evt)) { this.eventCoroutinesToRemove.Add(i); } else if (!c.IsWaitingForEvent()) { this.outstandingCoroutines.Enqueue(c); this.eventCoroutinesToRemove.Add(i); } } } /// /// Returns a list of all currently active objects under this handler. /// /// All active coroutines public IEnumerable GetActiveCoroutines() { return this.tickingCoroutines.Concat(this.eventCoroutines); } private void MoveOutstandingCoroutines() { var i = 0; this.eventCoroutines.RemoveAll(c => this.eventCoroutinesToRemove.Contains(i++)); this.eventCoroutinesToRemove.Clear(); while (this.outstandingCoroutines.Count > 0) { var coroutine = this.outstandingCoroutines.Dequeue(); var list = coroutine.IsWaitingForEvent() ? this.eventCoroutines : this.tickingCoroutines; var position = list.BinarySearch(coroutine); list.Insert(position < 0 ? ~position : position, coroutine); } } private static IEnumerator InvokeLaterImpl(Wait wait, Action action) { yield return wait; action(); } } }