x


Singletons with coroutines?

I have a singleton script to manage the state of a power level. A coroutine within the script regenerates the power back to 100% every second or two if the power level falls below 100%.

From what I can tell you can't use a coroutine on a script unless attached to a GO. I get the error "Coroutine couldn't be started because the the game object 'defaultProperties' is inactive!"

I attached the script to an empty GO but still have the error if it tries to run the coroutine. Is it possible to create a singleton that has coroutines?

public Singleton() { if (instance == null) { instance = this; } }

 public static Singleton Instance
 {
     get
     {
         if (instance == null)
         {
             new Singleton();
         }

         return instance;
     }
 }

 IEnumerator RegenPower() 

{

 yield return new WaitForSeconds(1); 
 AddPower(1);        
 StartCoroutine(RegenPower());

 }</code></pre>
more ▼

asked May 23, 2010 at 06:56 AM

chearner gravatar image

chearner
423 54 42 43

Actually, I don't see good reasons to use singleton in C#. http://stackoverflow.com/questions/10129680/what-should-we-use-for-global-variables-on-csharp-and-when-or-why-not

Maybe you could point me otherwise.

Apr 12, 2012 at 06:38 PM Cawas

Well, 2012 @Cawas, here are good reasons to use them: http://forum.unity3d.com/threads/197169-Singleton-vs-Static?p=1340284#post1340284 basically, they're safer and more flexible.

Aug 26, 2013 at 08:24 PM Cawas
(comments are locked)
10|3000 characters needed characters left

4 answers: sort voted first

As SpikeX already pointed out calling a co-routine recursively might not be a good idea. Using Singletons for global Behaviour and states is a common software architecture. However, you can only start Coroutines from MonoBehaviours and you can't use the "correct" singleton pattern, as you are not supposed to call or change the constructor of a MonoBehaviour.

There are several ways to write a "pseudo-singleton" that is a MonoBehaviour to use with Unity.

public class EasySingleton { private static EasySingleton instance = null; public static EasySingleton Instance { get { return instance; } }

void Awake() { instance = this; } }

That's the easiest way. However you will have to attach this to a GameObject manually in your Scene.

public class AdvancedSingleton : MonoBehaviour {

 private static AdvancedSingleton instance = null;
 public static AdvancedSingleton Instance { 
     get {
         if (instance == null)
         {
             Debug.Log("instantiate");
             GameObject go = new GameObject();
             instance = go.AddComponent&lt;AdvancedSingleton&gt;();
             go.name = "singleton";
         }

         return instance; 
     } 
 }

}

The advanced version will create its own GameObject and attach itself to it as soon as you call it for the first time. This is very convenient and keeps your scene small in the editor. You could also implement something that will remove the object from you scene once it isn't needed anymore (e.g. the coroutines have finished.)

more ▼

answered May 23, 2010 at 09:59 AM

StephanK gravatar image

StephanK
6.5k 116 106 158

I've used that second method a few times. It takes some getting used to, but works pretty well. One problem that's easy to overlook: level loading can destroy the instance, if you're not careful. Easy enough to fix by setting DontDestroyOnLoad or catching OnDestroy(), but it needs to be handled one way or another.

Apr 12, 2012 at 06:42 PM rutter
Aug 26, 2013 at 08:31 PM Cawas

All of this information posted was good to know. The pseudo-singleton you suggested works by creating the instance on the Start() event. The wiki that describes singletons could use updating as I tried them all...this added information makes it more clear, especially when dealing with MonoBehaviourss and coroutines. I'm able to use coroutines like I need, a recursive coroutine to regenerate my power level works well...waits a second or two and calls itself again indefinitely. A while loop doesn't give me control over the delay I want.

May 23, 2010 at 09:50 PM chearner

Using a while loop and calling the coroutine from within itself do exactly the same thing. You can just add a yield return new WaitForSeconds(1) into the while loop.

May 23, 2010 at 09:56 PM qJake

Thank you SpikeX, I will try that as well. If anyone knows which method may be the least taxing, it might be marginal but I'm targeting iPhones so any savings is worth the effort.

May 24, 2010 at 03:14 AM chearner
(comments are locked)
10|3000 characters needed characters left

I don't know why you're getting errors, but I see a lot of potential problems with this script. Let me point them out, and we'll hope that one of them was the problem.

  • I wouldn't call a coroutine from within a coroutine. Use a while(true) loop instead. It won't freeze your game because of the 1 second wait time. I do this all the time.
  • I don't think you implemented the Singleton pattern correctly. See the correct C# example here.. Your constructor needs to be private (and empty!), and you need a static reference to the Singleton class.
  • I don't think the singleton pattern will work for game objects that derive from MonoBehaviour. In fact, it's actually recommended that you DON'T have a constructor, and instead use Start() or Awake() for initialization. I can't even begin to wonder what kinds of problems you'd run into if you had a singleton attached to a game object.
  • If you went into a bit of detail as to why you thought you needed a singleton, someone here might be able to recommend the correct way of doing what you're trying to do.
more ▼

answered May 23, 2010 at 08:19 AM

qJake gravatar image

qJake
12.7k 92 136 214

Thanks. I was using this a script to manage all my power functions like addpower() reducepower() etc. I wanted to be able to call them from any other script like singleton.instance.addpower(). This would Also need to manage the gui display for a power bar.

May 23, 2010 at 09:04 AM chearner

I don't think you have to do the while true thing for the coroutine. It looks like it is just a recursive function his way instead of a loop. Thanks for some other good info though especially point 2 and 3.

May 23, 2010 at 07:25 PM Peter G

Actually there's a lot of no no on while(true) aswell, but I use it all the time aswell ^^

Dec 06, 2010 at 12:35 PM Proclyon

+1 There are several "Singleton" implementations out there that aren't really singletons. That said, I don't think it's the Singleton most people are after but a simple convenient access point to a common script, gameobject or component :) The "Componenton" :)

Mar 16, 2011 at 02:01 AM Statement

Using recursion where not necessary is a programming no-no. :P

May 23, 2010 at 09:56 PM qJake
(comments are locked)
10|3000 characters needed characters left

to make the Advanced singleton work,

in the start method, add a call to

DontDestroyOnLoad(go);

then, in the awake method add

if (instance) Destroy(gameObject);

instance = this;

when using writing public methods, be sure to add "Instance." to your variables. Also, make sure your public methods are static like this:

static public void someMethod()
{
   Instance.someVar = 100;
}

Beware that i may have made a few mistakes/type-os, but this is how I have my singleton that uses co-routines setup, and it works as it should. If anybpdy sees a real issue with this, speak up, so we can get this solved once and for all.

BTW, I learned this code from looking at other peoples code, so its my turn to pay it forward.

more ▼

answered Mar 16, 2011 at 12:10 AM

kablammyman gravatar image

kablammyman
107 6 6 12

this is true, but with the code, I have an init() method that i call before using the data. Its not ideal, but it works.

Mar 31, 2011 at 05:38 PM kablammyman

That's not really an ideal method, because Awake functions don't run in any defined order, so attempting to access the singleton from other Awake functions can and will fail.

Mar 16, 2011 at 01:23 AM Eric5h5
(comments are locked)
10|3000 characters needed characters left

Full explanation of the new "grid" system is now here ..

http://answers.unity3d.com/questions/551297/how-can-i-instantiate-a-prefab-from-a-static-funct.html


Purely for convenience:

In almost all projects: it will be the case that you will have an empty game object attached on your opening scene, for clarity let's call it "holdAll".

"holdAll" is persistent throughout the game.

Attached to that, you will have many files such as Shopping.js, Analytics.js, Calculations.js .. etc etc

Each of those uses coroutines. So they really are best as "normal" scripts attached to an game object.

Again, it's inevitable that almost every Unity game has an empty game object ("holdAll") which is persistent throughout the whole game, and you use those various

I've never seen a Unity project that does not have that.

So, this is all great and normal. But, each time you want to use a routine from shopping.js, you need to make a local variable which, annoyingly, you have to hook to shopping.js on holdAll.

Of course, it's much easier to just call ordinary static libraries, like when you use Mathf or the like.

Is there a solution?

Yes, here's what I do. Make a tiny little class called say "grid":

 class grid
     {
     static var shop:Shopping;
     static var etc:Etc;
     static var etc:Etc;
     
     private static function grid()
         {
         shop = GameObject.Find("holdAll").GetComponent(Shopping);
         etc = GameObject.Find("holdAll").GetComponent(Etc);
         etc = GameObject.Find("holdAll").GetComponent(Etc);
         }
         // nb, grid() is indeed magically called (once only)
         // when one of these vars is first accessed - 
         // you need do nothing, just use the vars at will.
     }

then from anywhere at all in your project,

with no further effort,

you can just write

 grid.shop.buy(..);
 grid.shop.sell(..);
 yield grid.shop.showDialog(...);
 yield grid.shop.interactionUser(...);
 x = grid.shop.count;

and so on. Same for all the others (Shopping.js, Analytics.js, Calculations.js etc etc)

I want the convenience of just being able to say

yield shop.whatever();

anywhere in the project WITHOUT needing an annoying hooked-up local variable "shopit" which leads to holdAll-Shopping.js.

But in my opinion there's just no really good, bulletproof, way to make a static/singleton/etc, where it's a "normal" coroutine-based Unity object.

So ... for me, this seems to answer all those needs. The only downside is effectively one extra "." when you type grid.shopping.

Seems to work ok ?

more ▼

answered May 04, 2013 at 10:39 AM

Fattie gravatar image

Fattie
27.2k 329 682 406

For the maintaining issue, I mean from experience not just reasoning. The problem is not just when you can follow the changes or when very descriptive build errors show you where to fix. The problem is when it's not that obvious, and takes a lot of time to find a small change.

When I was starting with Unity I also always thought hooking up the nodes on editor was also horrible, but now to me seems like a "puritanism of writing just code". I'd think "we can't see in our class where it is connected, need to go to the editor". Well, that's true, but it's still less painful than the maintenance issue.

But, once again, I'm not saying this is bullet-proof or preferred design. I simply don't know. I think I never really found any guide lines on this specific, and I'm still trying to separate good from bad there, and maybe find a best one.

May 15, 2013 at 01:57 PM Cawas

I guess most of my "hookups" are in self contained prefabs. So the most common things would be hooking up which UILabel is the score etc etc.

I use a "Memorise" system that hooks up other things (basically a script which works during awake to find common objects and then puts them in the right place). It's effectively find with tag but with no need to add "Tags" to the system I guess. I also use a lot of singletons I suppose.

May 15, 2013 at 02:02 PM whydoidoit

Hey Cawas! I'm so glad it's working for you. More and more, I honestly think this is the only approach that is solid. I'm not sure what you mean in particular about "static" - perhaps post an example of code you are using now? In my code above, Class Grid, you pretty much have to do it like I have it there, I'd say - so you can access it from anywhere. Let's see how you're doing it!

Aug 22, 2013 at 09:07 PM Fattie

@Fattie I really don't want to clutter up this comment space even further... Maybe this is something we could move on to the forums. I hate forums, but this is a very rare good example of something they are good for. ;-) http://forum.unity3d.com/threads/197169-Singleton-vs-Static

Aug 23, 2013 at 02:54 PM Cawas

Well, you know... I've adopted this idea to clean up our project... But without all the "static", just in case. I've got many problems using static for global vars before and this time I'll try without it. This means: singleton.

For now, just reporting a step... ;-)

Aug 22, 2013 at 06:08 PM Cawas
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x634
x96

asked: May 23, 2010 at 06:56 AM

Seen: 13983 times

Last Updated: Mar 12 at 07:25 AM