- Home /
How can I use Coroutines in ScriptableObject?
I can only use coroutines in MonoBehaviours. How can I workaround in order to get a similar behaviour in ScriptableObjects?
Answer by Dakwamine · Jun 29, 2012 at 07:25 PM
I can't see a registered user answer here (I will not mention his pseudonym as I am thinking it was intentional to delete it), but I received a mail alert of his sumbission which contained enough information to resolve my query.
The solution was to create another class which extends MonoBehaviour and then call StartCoroutine from this class. I don't use exactly what the user described in his answer but it gives the general idea:
Declare the coroutine using another MonoBehaviour. For example:
public class Example : MonoBehaviour {
public static Example instance;
void Start() {
Example.instance = this;
}
}
public class Scriptable : ScriptableObject {
public void doStuff() {
Example.instance.StartCoroutine(myCoroutine());
}
}
Thanks mister!
mmm But in that case you should Add an Example script to an Example Object in the scene, right?
So, it would nulify the ScriptableObject's main objective which is to avoid adding objects to the hierarchy...
Am i right?
In that case, you only need one GameObject with an Example $$anonymous$$onoBehaviour on it. This one is used by all the ScriptableObjects as the main "referrer". So you are right.
Umm yeah, you're right, at least, this way you only have to instance one object in the hyerarchy and not one for each script. $$anonymous$$aybe this is something Unity should think to feature in the future, isn't it?
This is a little misleading, since it's not the special way ScriptableObjects do it. Static classes have been using the same trick for a while: "put the coroutine on some monobehaviour and call it through a link" is the normal way.
Answer by ExtraMedicated · Apr 19, 2018 at 01:25 PM
In my case, I wanted to keep the coroutine in a simple class (not inheriting anything), but call the function which triggers the couroutine from a MonoBehaviour.
So what I did was pass the MonoBehaviour to the function that I wanted to start the coroutine from.
Here was my class that has the couroutine (Note that I don't put the name of the coroutine in quotes when calling StartCoroutine()):
public class VolumeChangeIndicator
{
private float timer, volume;
private float delay = 0.1F;
private bool running;
private AudioClip soundClip;
private AudioSource audioSource;
public VolumeChangeSound(AudioSource audioSource, AudioClip soundClip){
this.audioSource = audioSource;
this.soundClip = soundClip;
}
// This function triggers the coroutine.
public void VolumeSet(MonoBehaviour caller, float value){
volume = value;
timer = delay;
if (!running){
caller.StartCoroutine(VolChangeSound());
}
}
IEnumerator VolChangeSound(){
running = true;
while (timer > 0){
timer -= Time.unscaledDeltaTime;
yield return new WaitForEndOfFrame();
}
timer = 0;
audioSource.PlayOneShot(soundClip, volume);
running = false;
}
}
Then in my MonoBehavior, I did this:
public AudioClip volumeIndicatorSound;
private AudioSource audioSource;
private VolumeChangeIndicator volumeChangeIndicator;
void Start(){
audioSource = gameObject.AddComponent<AudioSource>();
// Instantiate the class...
volumeChangeIndicator = new VolumeChangeIndicator(audioSource, volumeIndicatorSound);
}
// This function is called by a slider every time the value is changed.
public void SetVolume(float value){
SetVolume(value, true);
}
public void SetVolume(float value, bool playSound){
PlayerPrefs.SetFloat("master_volume",value);
if (playSound){
// Pass this MonoBehaviour to the function so it can be used to run the coroutine.
volumeChangeIndicator.VolumeSet(this, value);
}
}
Yes this is also possible and a viable solution but keep in $$anonymous$$d that the coroutine's lifetime is bound to the $$anonymous$$onoBehaviour it's running on. If the $$anonymous$$onoBehaviour is disabled, deactivated or destroyed the coroutine would be ter$$anonymous$$ated. If you have several cases where you want to run a coroutine independent from a particular object it's usually the best to use a monobehaviour based singleton which never gets destroyed.
Answer by winxalex · Nov 10, 2015 at 02:22 PM
@Dakwamine You use ScriptableObject when you have creating asset on mind, so you can move across scenes and projects or load run-time. Binding to Scene GameObject's MonoBehaviour you are strangled to that Scene or scenes if you DontDestroy GameObject.
public class Scriptable : ScriptableObject {
GameObject PrefabGameObjectWithExampleMonoBehaviour;
public void doStuff() {
PrefabGameObjectWithExampleMonoBehaviour.GetComponent<Example>().StartCoroutine(myCoroutine());
}
@Bunny83 You not need to make instance of the PrefabGameObjectWithExampleMonoBehaviour by including Scriptable which is referencing PrefabGameObjectWithExampleMonoBehaviour, Unity creates hidden instance which when there is no other instance can be consider as singleton. }
You can now move .asset and prefab as pack and you wont lose functionality.
Edit: Code is pseudo!
Answer by astracat111 · Apr 11, 2017 at 07:40 PM
I thought you did like OnEnable() inside of the scriptable object...
Answer by michaelfelleisen · May 19, 2021 at 03:47 PM
I made a small forum thread about this which might be interesting. Its an alternative to the stuff I saw here and its an awesome in my opinion.
https://forum.unity.com/threads/scriptableobjects-and-coroutines.1112239/
Your answer
