simple trouble with static coroutines

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:

public static IEnumerator GetInRange(GameObject m, float mass){
//Do some stuff
//Call another routine to do some other stuff
yield return null;
}

ScriptB:

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!

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.

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 :slight_smile:

ScriptA, Routine1:

    public static IEnumerator GetElementsInRange(GameObject mX, float mX_mass){
        var Cols = Physics.OverlapSphere(mX.transform.position, mX_mass /* * gravityRange Formula*/);
     
        //CONFIGURE COMS
        foreach(Collider c in Cols){
            if(mX.collider != c && mX_mass >= c.GetComponent<VariableManager>().mass){
                if(mX_mass == c.GetComponent<VariableManager>().mass){
                    List<int> getID = new List<int>();
                    for(int i = 0; i < Cols.Length; i++){
                        getID.Add(Cols[i].GetComponent<VariableManager>().ID);
                    }
                    getID.Sort();
                 
                    if(mX.GetComponent<VariableManager>().ID == getID[0]){
                        StartCoroutine(DefineCoMS(mX, Cols));
                    }
                }
                else{
                    StartCoroutine(DefineCoMS(mX, Cols));
                }
            }
        }
        yield return null;
    }

ScriptB
```csharp
**[/B]void Update(){
//CALCULATE CoM
CoM = Vector3.zero;
mass = 0f;
for(int i = 0; i < _Matter.Count; i++){
var _mI = _Matter[i];
var _mI_mass = _mI.GetComponent().mass;

        CoM += _mI.rigidbody.worldCenterOfMass * _mI_mass;
        mass += _mI_mass;

        //Calculate subElement CoMS
        if(!name.Contains(_mI.name)){
            StartCoroutine (PhysicsManager2.GetElementsInRange(_mI, _mI_mass));
        }
    }
    CoM /= mass;
}[B]**

```

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

lines 16&20

    IEnumerator DefineCoMS(GameObject mX, Collider[] Cols){
        foreach (Collider c in Cols){
            ...
        }

        yield return null;
    }

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.

1 Like

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?

Here I fixed it using a parameter that you pass in the MonoBehaviour for. Note I also cleaned up

    public static IEnumerator GetElementsInRange(MonoBehaviour behaviour, GameObject mX, float mX_mass){
        var Cols = Physics.OverlapSphere(mX.transform.position, mX_mass /* * gravityRange Formula*/);
    
        //CONFIGURE COMS
        var mxVarManager = mx.GetComponent<VariableManager>();
      
        foreach(Collider c in Cols){
            var colliderVarManager = c.GetComponent<VariableManager>();
            if(mX.collider != c && mX_mass >= colliderVarManager.mass){
                if(mX_mass == colliderVarManager.mass){
                    List<int> getID = new List<int>();
                    for(int i = 0; i < Cols.Length; i++){
                        getID.Add(Cols[i].GetComponent<VariableManager>().ID);
                    }
                    getID.Sort();
                
                    if(mxVarManager.ID == getID[0]){
                        behaviour.StartCoroutine(DefineCoMS(mX, Cols));
                    }
                }
                else{
                    behaviour.StartCoroutine(DefineCoMS(mX, Cols));
                }
            }
        }
        yield return null; //why even have this?
    }

You’d basically call it in some script like this:

this.StartCoroutine(MyStaticClass.GetElementsInRange(this, mxGameObject, someMass));

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.

I think maybe it’s the singleton? …whatever that is. I’ll have to read-up on it. Thanks a bunch guys

Singleton?

Why a Singleton?

Are you just using design ideas because you’ve heard the name before?

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. :slight_smile:

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ā€

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.

    public static void DefineCoMSInGravitationalRange(GameObject mX, float mX_mass){
        var Cols = Physics.OverlapSphere(mX.transform.position, mX_mass /* * gravityRange Formula*/);
    
        //CONFIGURE COMS
        var mxVarManager = mx.GetComponent<VariableManager>();
      
        foreach(Collider c in Cols){
            var colliderVarManager = c.GetComponent<VariableManager>();
            if(mX.collider != c && mX_mass >= colliderVarManager.mass){
                if(mX_mass == colliderVarManager.mass){
                    List<int> getID = new List<int>();
                    for(int i = 0; i < Cols.Length; i++){
                        getID.Add(Cols[i].GetComponent<VariableManager>().ID);
                    }
                    getID.Sort();
                
                    if(mxVarManager.ID == getID[0]){
                        mxVarManager.StartCoroutine(DefineCoMS(mX, Cols));
                    }
                }
                else{
                    mxVarManager.StartCoroutine(DefineCoMS(mX, Cols));
                }
            }
        }
    }

Called as:

MyStaticClass.DefineCoMSInGravitationalRange(targetGameObject, targetMass);

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.

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.

hahah yeah that works way better…
damn I feel dumb.