Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

simple trouble with static coroutines

Discussion in 'Scripting' started by darkAbacus247, Jan 5, 2015.

  1. darkAbacus247

    darkAbacus247

    Joined:
    Sep 7, 2010
    Posts:
    251
    Hey all,
    hopefully this is quick and easy :

    scriptA contains a coroutine and is the only copy in the scene. It accesses this routine, performs some junk then calls upon other coroutines to calculate some other junk. Works just fine.

    scriptB needs access to this routine as well ((it is attached to a separate gameObject)).

    ScriptA:
    Code (CSharp):
    1. public static IEnumerator GetInRange(GameObject m, float mass){
    2. //Do some stuff
    3. //Call another routine to do some other stuff
    4. yield return null;
    5. }
    ScriptB:
    Code (CSharp):
    1. StartCoroutine (ScriptA.GetInRange(m, mass));
    Before the addition of scriptB, scriptA works 100%. The only difference in code is that I've added "static" to scriptA's IEnumerator so that ScriptB may find it without an object reference. Though, with this it's now causing the coroutines which scriptA calls to fail, stating that :

    An object reference is required to access non-static member

    They do not error without the addition of scriptB and the "static" mentioned above AND the object in question in-fact is scriptA's gameobject, thus a object reference should not be required. Because it is "my own object". Additionally, I've tried making these routines static as well, though it shouldn't matter, and doesn't change anything...so I guess It doesn't matter. Either way, I'm stumped.

    Any suggestions would be great, thanks in advance!
     
  2. Kirk Clawson

    Kirk Clawson

    Joined:
    Nov 4, 2014
    Posts:
    65
    A static method - which includes static coroutines - can only access other static methods or static fields. My suspicion is that you are accessing a member field or member function in your coroutine that you've hidden with the two comments.
     
  3. darkAbacus247

    darkAbacus247

    Joined:
    Sep 7, 2010
    Posts:
    251
    Here is the full version of the code (still needs refining), if you have the time to direct me in correcting this I would greatly appreciate it :)

    ScriptA, Routine1:
    Code (CSharp):
    1.  
    2.     public static IEnumerator GetElementsInRange(GameObject mX, float mX_mass){
    3.         var Cols = Physics.OverlapSphere(mX.transform.position, mX_mass /* * gravityRange Formula*/);
    4.      
    5.         //CONFIGURE COMS
    6.         foreach(Collider c in Cols){
    7.             if(mX.collider != c && mX_mass >= c.GetComponent<VariableManager>().mass){
    8.                 if(mX_mass == c.GetComponent<VariableManager>().mass){
    9.                     List<int> getID = new List<int>();
    10.                     for(int i = 0; i < Cols.Length; i++){
    11.                         getID.Add(Cols[i].GetComponent<VariableManager>().ID);
    12.                     }
    13.                     getID.Sort();
    14.                  
    15.                     if(mX.GetComponent<VariableManager>().ID == getID[0]){
    16.                         StartCoroutine(DefineCoMS(mX, Cols));
    17.                     }
    18.                 }
    19.                 else{
    20.                     StartCoroutine(DefineCoMS(mX, Cols));
    21.                 }
    22.             }
    23.         }
    24.         yield return null;
    25.     }
    26.  
    ScriptB
    Code (CSharp):
    1. [/B]void Update(){
    2.         //CALCULATE CoM
    3.         CoM = Vector3.zero;
    4.         mass = 0f;
    5.         for(int i = 0; i < _Matter.Count; i++){
    6.             var _mI = _Matter[i];
    7.             var _mI_mass = _mI.GetComponent<VariableManager>().mass;
    8.  
    9.             CoM += _mI.rigidbody.worldCenterOfMass * _mI_mass;
    10.             mass += _mI_mass;
    11.  
    12.             //Calculate subElement CoMS
    13.             if(!name.Contains(_mI.name)){
    14.                 StartCoroutine (PhysicsManager2.GetElementsInRange(_mI, _mI_mass));
    15.             }
    16.         }
    17.         CoM /= mass;
    18.     }[B]

    scriptA lines 16 & 20, if commented out everything works just fine.

    lines 16&20
    Code (CSharp):
    1.     IEnumerator DefineCoMS(GameObject mX, Collider[] Cols){
    2.         foreach (Collider c in Cols){
    3.             ...
    4.         }
    5.  
    6.         yield return null;
    7.     }
     
    Last edited: Jan 5, 2015
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    It's your calls to 'StartCoroutine'.

    'StartCoroutine' is a local member of MonoBehaviour, so you need a reference to a MonoBehaviour to call it.

    Have a parameter on your static method that accepts the MonoBehaviour needed, and call StartCoroutine on that.

    Or you could call StartCoroutine one of the available components you pull in the function... like the VariableManager. Noting though that the coroutine can be cancelled if that GameObject/Component gets disabled/deleted. Usually you want the MonoBehaviour that started this Coroutine to also start the sub Coroutines... so that way there is some predictable behaviour.
     
    Mycroft likes this.
  5. darkAbacus247

    darkAbacus247

    Joined:
    Sep 7, 2010
    Posts:
    251
    Ah, ok...starting to make sense
    I'm still kinda new with using routines, would you have an easy/quick example of this? or link even?
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Here I fixed it using a parameter that you pass in the MonoBehaviour for. Note I also cleaned up

    Code (csharp):
    1.  
    2.     public static IEnumerator GetElementsInRange(MonoBehaviour behaviour, GameObject mX, float mX_mass){
    3.         var Cols = Physics.OverlapSphere(mX.transform.position, mX_mass /* * gravityRange Formula*/);
    4.    
    5.         //CONFIGURE COMS
    6.         var mxVarManager = mx.GetComponent<VariableManager>();
    7.      
    8.         foreach(Collider c in Cols){
    9.             var colliderVarManager = c.GetComponent<VariableManager>();
    10.             if(mX.collider != c && mX_mass >= colliderVarManager.mass){
    11.                 if(mX_mass == colliderVarManager.mass){
    12.                     List<int> getID = new List<int>();
    13.                     for(int i = 0; i < Cols.Length; i++){
    14.                         getID.Add(Cols[i].GetComponent<VariableManager>().ID);
    15.                     }
    16.                     getID.Sort();
    17.                
    18.                     if(mxVarManager.ID == getID[0]){
    19.                         behaviour.StartCoroutine(DefineCoMS(mX, Cols));
    20.                     }
    21.                 }
    22.                 else{
    23.                     behaviour.StartCoroutine(DefineCoMS(mX, Cols));
    24.                 }
    25.             }
    26.         }
    27.         yield return null; //why even have this?
    28.     }
    29.  
    You'd basically call it in some script like this:

    Code (csharp):
    1.  
    2. this.StartCoroutine(MyStaticClass.GetElementsInRange(this, mxGameObject, someMass));
    3.  
    Here's the thing though... I'm not sure what the heck is going on here.

    Why is this static? Is there a reason you made the function static? Is this in a static class somewhere?

    Why is this a coroutine? You don't do any coroutine type stuff in the function, other than start other coroutines. I see you have 'yield return null' at the end of the function... but what's the point? The bulk of code still runs in a single frame. Why not just make this a function?

    Why is this function called 'GetElementsInRange' if the function doesn't return any elements in range? The name of a function should describe what it does... that name implies to me that it returns all elements in a range. When really it does nothing of the sort.
     
  7. darkAbacus247

    darkAbacus247

    Joined:
    Sep 7, 2010
    Posts:
    251
    I think maybe it's the singleton? ...whatever that is. I'll have to read-up on it. Thanks a bunch guys
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Singleton?

    Why a Singleton?

    Are you just using design ideas because you've heard the name before?
     
  9. darkAbacus247

    darkAbacus247

    Joined:
    Sep 7, 2010
    Posts:
    251
    Static / Coroutine: Mainly because I'm passing vars into the code from multiple areas and figure writing the code once must be better than re-righting the same code a few times, the only thing that changes are the variables passed into it. Lol I'm no expert, maybe there's a better way about this...but it seems logical to me. Less code must be better code...right?

    Additionally, it's static because scriptB appears on several objects, so the use of static allows me to then find the routine within scriptA without having extra overhead of looking for the object as well.

    GetElementsInRange: Line 3, each gameObject passed into this routine then grabs all other gameobjects within a given range. So in fact, it is getting elements within a range. :)

    Singleton: I dunno, just did a search off what you guys mentioned...that popped up and seemed along the right lines. But now I have to review your latest response to see what I can pull from that

    Yeild Return Null: Because it errors out when I don't? "not all code paths return a value"
     
    Last edited: Jan 5, 2015
  10. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Static - so this is a function multiple different classes can call? Then yeah, static is useful for that. Like we said, a static method will require context to access object level members (i.e. the call to 'StartCoroutine').

    GetElementsInRange - sure that's what the implementation does, but that's not what the output/result of the function is. A 'Get' style function implies that it returns something, which this doesn't (well it sort of does, with the yield return null, but we'll get to that later)

    Singleton - no, just no, I'm not going to get into this one. Singleton has its time and place, and this is NOT it.

    Yield return null - it throws an error because you have no other yield statements. But if you have no other yield statements, why is it a Coroutine in the first place? Your function doesn't iterate... so it's functionally NOT a coroutine. You could just make the function return 'void' and call it like any normal function.


    This functionally will accomplish the same thing. Note, I renamed it to something that describes what it's doing, as best as I can tell from the context I'm given.

    Code (csharp):
    1.  
    2.     public static void DefineCoMSInGravitationalRange(GameObject mX, float mX_mass){
    3.         var Cols = Physics.OverlapSphere(mX.transform.position, mX_mass /* * gravityRange Formula*/);
    4.    
    5.         //CONFIGURE COMS
    6.         var mxVarManager = mx.GetComponent<VariableManager>();
    7.      
    8.         foreach(Collider c in Cols){
    9.             var colliderVarManager = c.GetComponent<VariableManager>();
    10.             if(mX.collider != c && mX_mass >= colliderVarManager.mass){
    11.                 if(mX_mass == colliderVarManager.mass){
    12.                     List<int> getID = new List<int>();
    13.                     for(int i = 0; i < Cols.Length; i++){
    14.                         getID.Add(Cols[i].GetComponent<VariableManager>().ID);
    15.                     }
    16.                     getID.Sort();
    17.                
    18.                     if(mxVarManager.ID == getID[0]){
    19.                         mxVarManager.StartCoroutine(DefineCoMS(mX, Cols));
    20.                     }
    21.                 }
    22.                 else{
    23.                     mxVarManager.StartCoroutine(DefineCoMS(mX, Cols));
    24.                 }
    25.             }
    26.         }
    27.     }
    28.  
    Called as:

    Code (csharp):
    1.  
    2. MyStaticClass.DefineCoMSInGravitationalRange(targetGameObject, targetMass);
    3.  
    In it I'm just using the VariableManager to start the coroutine with... figured it'd work just fine. Again, lacking any idea of what your design is, I couldn't make a cleaner choice, so I just went with an adhoc approach. It appears that's your design principal... throw code at it until it works.
     
    Last edited: Jan 5, 2015
  11. darkAbacus247

    darkAbacus247

    Joined:
    Sep 7, 2010
    Posts:
    251
    Ha, thanks I'll look through this and see what I can do to correct my code. And thank you for the code response I def. see how that's a better approach, makes a lot more sense that way...unfortunately using a routine was the first idea I had and didn't bother looking further, I mean technically like you said it does work...just not the best approach. Agree'd.

    With that being said, my method is certainly not chuck stuff together n hope for the best. But if you don't know, you don't know...isn't that the way things go? At least until you do know that is? I can't very well google search a result if I haven't the proper background to know what to search for. Self taught for the loss, I suppose.

    So, now I know...and I'll do better in the future : )
    Either way, thank you. This should help.
     
  12. darkAbacus247

    darkAbacus247

    Joined:
    Sep 7, 2010
    Posts:
    251
    hahah yeah that works way better...
    damn I feel dumb.