1
\$\begingroup\$

I have a class which is not a monobehaviour (lets call it "Generation"), and as such running CoRoutines from within are not an option.

I have a monobehaviour (lets call this "TestScript") which needs to call a function within Generation. One parameter required is a Func>. It then calls that Func multiple times, expecting a result in the form of a double. The problem, is that it takes time to return, it needs to run across many frames, and so it kicks off some Coroutines (as this is in the monobehaviour). I set it to return a Task so that I could use Threading.SpinWait.SpinUntil and only return after the coroutines have exited, but this just hangs the main Unity thread.

Here is the function I am calling:

        public void AssessGeneration(Func<NeuralNet, Task<double>> assessment)
        {
            foreach (NeuralNet neuralNet in neuralNets)
            {
                neuralNet.Fitness = assessment(neuralNet).Result;
                neuralNet.fitnessAssessed = true;
            }
        }

So, I've tried many other things, such as moving the SpinWait into the Generation class, but still hangs. I've tried calling the function in Generation within a Task on a new thread, but when it attempts to run the coroutines it complains about not being on the main thread.

So - I have a function, in a class not inheriting from MonoBehaviour. It is called from a MonoBehaviour, and must pass in a function which takes a parameter of type NeuralNet, and returns a double. It must return a double after a coroutine has finished, but I can't be blocking the main thread.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I would suggest creating a Singleton GameObject responsible for running your coroutine, or simply pass a gameobject to the AssessGeneration function (the one holding the TestScript component, and calling the AssessGeneration function). \$\endgroup\$
    – Hellium
    Mar 12, 2018 at 9:45
  • \$\begingroup\$ Can your generation work itself run on a different thread, rather than using coroutines at all? Or do you need access to content available only on the main thread? \$\endgroup\$
    – DMGregory
    Mar 12, 2018 at 13:40

1 Answer 1

3
\$\begingroup\$

It's not true that you can't make a method in a base class working as a coroutine.

If I understood you correctly, the AssessGeneration method is inside your base class, Generation, and you want it to be able to yield.

In order to do so, make its return type an IEnumerator instead of void:

public IEnumerator AssessGeneration(Func<NeuralNet, Task<double>> assessment) {}

then call it from your MonoBehaviour class TestScript using StartCoroutine, so you can use yield as usual inside AssessGeneration.

Simple example:

Generation.cs

using System.Collections;

public class Generation {
    public IEnumerator AssessGeneration(/*Your Parameters*/) {
        //Your code
    }
}

TestScript.cs

using System.Collections;
using UnityEngine;

public class TestScript : MonoBehaviour {

    Generation generationInstance = new Generation();
    IEnumerator myCoroutine;

    private void Start () {     
        myCoroutine = generationInstance.AssessGeneration(/*Your Parameters*/);
        StartCoroutine(myCoroutine);
    }
}

EDIT

After your comment, here's the code if you have two overloads of the same method.

Generation.cs

using System.Collections;
using UnityEngine;

public class Generation {
    //This is the overload called from TestScript
    public IEnumerator AssessGeneration(/*Your parameters*/) {
        //Code before calling the second overload
        yield return AssessGeneration(/*Func parameter*/);
        //Code after completion of the second overload
    }

    //Second overload with just the Func parameter
    //If this overload is never called from outside leave it private, otherwise public
    private IEnumerator AssessGeneration(/*Func parameter*/) {
        //Your code
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ I kind of get it, but I can't figure out how to apply that to my situation as I explained it a little simpler than it actually was. Within Generation, I have AssessGeneration with 2 different signatures - I am calling the first, which takes a number of parameters, and passes one of those (the Func<NeuralNet, Task<double>>) onto the other one. So I am calling a void function within that class, but that is the one which needs to wait for a coroutine to end. \$\endgroup\$
    – pingu2k4
    Mar 12, 2018 at 23:33
  • \$\begingroup\$ Ok, so you have two overloads of AssessGeneration, I'll edit my answer to show you how it's done in this case. \$\endgroup\$
    – Galandil
    Mar 13, 2018 at 13:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .