A coworker and I recently fell into the ScriptableObject rabbit hole and thought it would be awesome if you could start Coroutines on ScriptableObjects. At the moment this is not possible and I do not see the Unity team changing this in the near future.
So, why do we even need this? In our case we wanted to have an authentication ScriptableObject which sends a webrequests to authenticate a user. There are a few workarounds to this but we did not like them a lot, but I will list them anyways.
- Have some GameObject with a script attached in the scene. Find the script instance from the Scriptable Object with FindObjectOfType() or other ways to search your scene and trigger a public function on said script.
- Have a singleton GameObject in your scene to skip the scene searching step by accessing the static T Instance attribute and call the public function from there.
- Donât use ScriptableObjects at all, be forever stuck in MonoBehaviour/Singleton-land and lose your will to live after trying to merge multiple git branches with overlapping Scene/ Prefab/ Nested-Prefab changes and hard-linked MonoBehaviours
So we came up with another option. The basic idea is that a ScriptableObject which wants to run a Coroutine spawns a GameObject in the Scene which will hold the Coroutine until it is finished and destroys itself afterwards.
Inherit your custom ScriptableObject from SO_Base instead of ScriptableObject and use StartCoroutine the same way you would in a normal MonoBehaviour.
using System.Collections;
using UnityEngine;
public class SO_Custom : SO_Base
{
public void SomeFunction(int _a, int _b)
{
StartCoroutine(SomeHeavyCalculations(_a, _b));
}
private IEnumerator SomeHeavyCalculations(int _a, int _b)
{
yield return new WaitForSeconds(5);
Debug.Log(_a + _b);
}
}
using System.Collections;
using UnityEngine;
public class SO_Base : ScriptableObject
{
protected void StartCoroutine(IEnumerator _task)
{
if (!Application.isPlaying)
{
Debug.LogError("Can not run coroutine outside of play mode.");
return;
}
CoWorker coworker = new GameObject("CoWorker_" + _task.ToString()).AddComponent<CoWorker>();
coworker.Work(_task);
}
}
using System.Collections;
using UnityEngine;
public class CoWorker : MonoBehaviour
{
public void Work(IEnumerator _coroutine)
{
StartCoroutine(WorkCoroutine(_coroutine));
}
private IEnumerator WorkCoroutine(IEnumerator _coroutine)
{
yield return StartCoroutine(_coroutine);
Destroy(this.gameObject);
}
}
What do you guys think?