Skip to content

Instantly share code, notes, and snippets.

@TobiasPott
Last active September 23, 2019 12:57
Show Gist options
  • Save TobiasPott/6f5945313d9798e30ad4512fd24e0ec7 to your computer and use it in GitHub Desktop.
Save TobiasPott/6f5945313d9798e30ad4512fd24e0ec7 to your computer and use it in GitHub Desktop.
Helper class to execute Unity3D Coroutines without an explicit MonoBehaviour reference
using System.Collections;
#if NET_4_6 || NET_STANDARD_2_0
using System.Threading.Tasks;
#endif
using UnityEngine;
public class Coroutiner : MonoBehaviour
{
private static Coroutiner _instance = null;
public static Coroutiner Instance
{
get
{
if (_instance == null)
{
GameObject goInst = new GameObject("Coroutiner", typeof(Coroutiner));
_instance = goInst.GetComponent<Coroutiner>();
GameObject.DontDestroyOnLoad(goInst);
}
return _instance;
}
}
private void Awake()
{
if (_instance == null || _instance == this)
_instance = this;
else
GameObject.Destroy(this.gameObject);
GameObject.DontDestroyOnLoad(this.gameObject);
}
public static Coroutine Start(IEnumerator routine)
{ return Coroutiner.Instance.StartCoroutine(routine); }
public static void Stop(IEnumerator routine)
{ Coroutiner.Instance.StopCoroutine(routine); }
public static void Stop(Coroutine routine)
{ Coroutiner.Instance.StopCoroutine(routine); }
public static void StopAll()
{ Coroutiner.Instance.StopAllCoroutines(); }
/*
public class ThreadParameters
{
public const string PN_Mutex = "_internal_Mutex";
private Mutex _mutex = null;
private Dictionary<string, object> _params = new Dictionary<string, object>();
public Mutex Mutex
{ get { return _mutex; } }
public object this[string key]
{ get { return _params[key]; } }
public ThreadParameters()
{
_mutex = new Mutex();
_params.Add(PN_Mutex, _mutex);
}
public void AddParameter(string name, object value)
{ if (!_params.ContainsKey(name)) _params.Add(name, value); }
public void RemoveParameter(string name)
{ if (_params.ContainsKey(name)) _params.Remove(name); }
}
public static void StartAsThread(Action<object> target, ThreadParameters parameters)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(target), parameters);
//return mutex;
}
*/
// ========================
// paste this into a active behaviour to test threaded processing of stuff
// ========================
//private void Update()
//{
// if (Input.GetKeyDown(KeyCode.T))
// {
// this.StartCoroutine(Coroutiner.COR_StartThread(Coroutiner.UseResource));
// }
//}
/*
public static IEnumerator COR_StartThread(Action<object> task)
{
Coroutiner.WaitCallbackParameters threadParams = new Coroutiner.WaitCallbackParameters();
System.Threading.Mutex mutex = threadParams.Mutex;
Coroutiner.StartAsThread(task, threadParams);
Debug.Log("Thread started: " + Time.time);
System.Threading.WaitHandle[] waitHandles = new System.Threading.WaitHandle[] { mutex };
do { yield return null; }
while (!System.Threading.Mutex.WaitAll(waitHandles, 33));
Debug.Log("Thread ended: " + Time.time);
}
public static Vector3 _result = Vector3.zero;
// This method represents a resource that must be synchronized
// so that only one thread at a time can enter.
public static void UseResource(object objectInfo)
{
Coroutiner.WaitCallbackParameters parameters = (Coroutiner.WaitCallbackParameters)objectInfo;
System.Threading.Mutex mutex = parameters.Mutex;
mutex.WaitOne();
// Wait until it is safe to enter.
Debug.Log("{0} is requesting the mutex");
Debug.Log("{0} has entered the protected area");
// Place code to access non-reentrant resources here.
// Simulate some work.
//System.Threading.Thread.Sleep(500);
System.Random rnd = new System.Random();
for (int i = 0; i < 10000000; i++)
{
_result += Vector3.up * (float)(rnd.NextDouble() * 2 - 2);
}
Debug.Log("{0} is leaving the protected area");
// Release the Mutex.
mutex.ReleaseMutex();
Debug.Log("{0} has released the mutex: " + _result.ToString("0.000"));
}
*/
}
#if NET_4_6 || NET_STANDARD_2_0
public class TaskResult
{
public System.Threading.Tasks.TaskStatus Status { get; set; } = System.Threading.Tasks.TaskStatus.Created;
}
public class TaskResult<T> : TaskResult
{
public T Value { get; set; }
}
public static class CoroutinerExtensions
{
/// <summary>
/// converts a <c>System.Threading.Tasks.Task<T></c> to be run as a Unity coroutine
/// </summary>
/// <typeparam name="T">The type to return from the task process</typeparam>
/// <param name="task">The task instance to convert into a coroutine</param>
/// <param name="result">A <c>TaskResult<T></c> which stores the tasks result and final status.</param>
/// <returns>An enumerator to be used as a coroutine in Unity</returns>
public static IEnumerator ToCoroutine<T>(this System.Threading.Tasks.Task<T> task, TaskResult<T> result)
{
yield return null;
do
{ yield return null; }
while (!HasTaskFinished(task) && !task.IsFaulted && !task.IsCanceled);
result.Status = task.Status;
if (result.Status == System.Threading.Tasks.TaskStatus.RanToCompletion)
result.Value = task.Result;
}
/// <summary>
/// converts a <c>System.Threading.Tasks.Task</c> to be run as a Unity coroutine
/// </summary>
/// <param name="task">The task instance to convert into a coroutine</param>
/// <param name="result">A <c>TaskResult</c> which stores the tasks result and final status.</param>
/// <returns></returns>
public static IEnumerator ToCoroutine(this Task task, TaskResult result)
{
yield return null;
do
{ yield return null; }
while (!HasTaskFinished(task));
result.Status = task.Status;
}
private static bool HasTaskFinished(System.Threading.Tasks.Task task)
{
switch (task.Status)
{
// check for the task status to determine it has finished (either errored, cancelled or completed)
case System.Threading.Tasks.TaskStatus.Canceled:
case System.Threading.Tasks.TaskStatus.Faulted:
case System.Threading.Tasks.TaskStatus.RanToCompletion:
return true;
}
return false;
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment