Last active
August 16, 2022 22:12
-
-
Save R3tr0BoiDX/1f58d5c2285ba2daf7cd13ac7f67f95e to your computer and use it in GitHub Desktop.
Convenient coroutine API for Unity by krockot, extended by R3tr0BoiDX (that's me, heh)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
namespace Tools { | |
/// <summary> | |
/// Addendum to krockot's original task manager - https://github.com/krockot/Unity-TaskManager | |
/// Added possibility to pool a bunch of tasks together, so they can be started, stopped,... together | |
/// </summary> | |
public class TaskPool { | |
public delegate void AllFinishedHandler(); | |
private readonly List<Task> tasks = new(); | |
private bool finished; | |
private int finishedTaskCounter; | |
public bool AllRunning => CheckIfAllAreRunning(); | |
public bool AllPaused => CheckIfAllArePaused(); | |
private bool PoolLocked { get; set; } | |
/// <summary> | |
/// Gets triggered when all tasks finished. | |
/// </summary> | |
public event AllFinishedHandler AllFinished; | |
/// <summary> | |
/// Add a task to the pool | |
/// </summary> | |
/// <param name="_task">The task, that should be added to the pool</param> | |
/// <returns> | |
/// Indicates if a task could be added successfully to the pool. | |
/// If pool is already running, it gets locked and no further task can be added. | |
/// </returns> | |
public bool Add(PoolTask _task) { | |
if (PoolLocked) { | |
return false; | |
} | |
tasks.Add(_task); | |
_task.Finished += ATaskFinished; | |
return true; | |
} | |
/// <summary> | |
/// Start all tasks within the pool together | |
/// </summary> | |
public void StartAll() { | |
PoolLocked = true; | |
foreach (Task task in tasks) { | |
task.Start(); | |
} | |
} | |
/// <summary> | |
/// Stop all the tasks within the pool together | |
/// </summary> | |
public void StopAll() { | |
foreach (Task task in tasks) { | |
task.Stop(); | |
} | |
} | |
/// <summary> | |
/// Pause all the tasks in the pool | |
/// </summary> | |
public void PauseAll() { | |
foreach (Task task in tasks) { | |
task.Pause(); | |
} | |
} | |
/// <summary> | |
/// Resume all the tasks that are paused in the pool | |
/// </summary> | |
public void UnpauseAll() { | |
foreach (Task task in tasks) { | |
task.Unpause(); | |
} | |
} | |
private bool CheckIfAllAreRunning() { | |
bool allRunning = true; | |
foreach (Task unused in tasks.Where(_task => !_task.Running)) { | |
allRunning = false; | |
} | |
return allRunning; | |
} | |
private bool CheckIfAllArePaused() { | |
bool allPaused = true; | |
foreach (Task unused in tasks.Where(_task => !_task.Paused)) { | |
allPaused = false; | |
} | |
return allPaused; | |
} | |
private void ATaskFinished(bool _manual) { | |
finishedTaskCounter++; | |
if (!finished && finishedTaskCounter == tasks.Count) { | |
finished = true; | |
AllFinishedHandler handler = AllFinished; | |
handler?.Invoke(); | |
} | |
} | |
} | |
/// <summary> | |
/// A Task object represents a coroutine. Tasks can be started, paused, and stopped. | |
/// It is an error to attempt to start a task that has been stopped or which has naturally terminated. | |
/// </summary> | |
public class Task { | |
/// <summary> | |
/// Delegate for termination subscribers. Manual is true if and only if | |
/// the coroutine was stopped with an explicit call to Stop(). | |
/// </summary> | |
public delegate void FinishedHandler(bool _manual); | |
private readonly TaskManager.TaskState task; | |
/// <summary> | |
/// </summary> | |
/// <summary> | |
/// Creates a new Task object for the given coroutine. | |
/// If autoStart is true (default) the task is automatically started upon construction. | |
/// </summary> | |
public Task(IEnumerator _task, bool _autoStart = true) { | |
task = TaskManager.CreateTask(_task); | |
task.Finished += TaskFinished; | |
if (_autoStart) { | |
Start(); | |
} | |
} | |
/// <summary> | |
/// Returns true if and only if the coroutine is running. Paused tasks are considered to be running. | |
/// </summary> | |
public bool Running => task.Running; | |
/// <summary> | |
/// Returns true if and only if the coroutine is currently paused. | |
/// </summary> | |
public bool Paused => task.Paused; | |
/// <summary> | |
/// Termination event. Triggered when the coroutine completes execution. | |
/// </summary> | |
public event FinishedHandler Finished; | |
/// <summary> | |
/// Begins execution of the coroutine | |
/// </summary> | |
public void Start() { | |
task.Start(); | |
} | |
/// <summary> | |
/// Discontinues execution of the coroutine at its next yield. | |
/// </summary> | |
public void Stop() { | |
task.Stop(); | |
} | |
public void Pause() { | |
task.Pause(); | |
} | |
public void Unpause() { | |
task.Unpause(); | |
} | |
private void TaskFinished(bool _manual) { | |
FinishedHandler handler = Finished; | |
handler?.Invoke(_manual); | |
} | |
} | |
/// <summary> | |
/// Specialized task for pool, with no auto start | |
/// This way, all tasks within the pool can be started together | |
/// </summary> | |
public class PoolTask : Task { | |
public PoolTask(IEnumerator _task) : base(_task, false) { } | |
} | |
internal class TaskManager : MonoBehaviour { | |
private static TaskManager instance; | |
public static TaskState CreateTask(IEnumerator _coroutine) { | |
if (instance != null) { | |
return new TaskState(_coroutine); | |
} | |
GameObject go = new("TaskManager"); | |
instance = go.AddComponent<TaskManager>(); | |
return new TaskState(_coroutine); | |
} | |
public class TaskState { | |
public delegate void FinishedHandler(bool _manual); | |
private readonly IEnumerator coroutine; | |
private bool stopped; | |
public TaskState(IEnumerator _task) { | |
coroutine = _task; | |
} | |
public bool Running { get; private set; } | |
public bool Paused { get; private set; } | |
public event FinishedHandler Finished; | |
public void Pause() { | |
Paused = true; | |
} | |
public void Unpause() { | |
Paused = false; | |
} | |
public void Start() { | |
Running = true; | |
instance.StartCoroutine(CallWrapper()); | |
} | |
public void Stop() { | |
stopped = true; | |
Running = false; | |
} | |
private IEnumerator CallWrapper() { | |
yield return null; | |
IEnumerator e = coroutine; | |
while (Running) { | |
if (Paused) { | |
yield return null; | |
} else { | |
if (e != null && e.MoveNext()) { | |
yield return e.Current; | |
} else { | |
Running = false; | |
} | |
} | |
} | |
FinishedHandler handler = Finished; | |
handler?.Invoke(stopped); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment