the USE-CASE is that sometimes I want to call the code I have on my coroutine, but I dont want it to yield, I want it to run the whole code and skip all the yields
is it best practice to just make a new method with the same name that is not ienumerator, or can I still call the coroutine, just without the startcoroutine() syntax?
Calling a coroutine like a normal method won’t actually do anything. It just returns an IEnumerator object, but unless you call MoveNext() on the IEnumerator the code in the coroutine won’t be executed.
You could do this:
IEnumerator coroutine = MyCoroutine(); // create the coroutine
while (coroutine.MoveNext()) {} // execute it until the very end
All StartCoroutine() does is pass the IEnumerator to Unity, and tell it to automatically advance the coroutine until it’s done.
Either execute the coroutine manually as I suggested above, or make a method with a different name. You can’t have two methods with the same name and same parameters, because it is impossible for the compiler to know which one you’re calling.
Ran into this problem too occasionally. There isn’t a way to reuse the method and somehow treat it sometimes as a non-coroutine. That’s because there’s some compiler magic happening in the background when yield statements are involved.
Best solution is to do what arkano22 said, but you can wrap that into an universal custom helper function which contains that while loop.
Something like: void CoroutineAsFunc(IEnumerator the_coroutine) (can’t write it out right now from phone).
One old trick is to turn everything it does into it’s own function and let the co-routine/non-co-routine be drivers:
IEnum myCoroutine() {
while( .. ) {
stuff(); // these used to be lines in the coroutine
yield return ...
}
}
Now you can call stuff(); to run the “coroutine” just one time.
Of course that won’t work very well if your coroutine looks like "important variables, stuff, yield, change variables, stuff2, yield … ". In that case a hack might be adding an “instant” flag:
IEnum myCoroutine(bool runWithYields=true) {
...
if(runWithYields) yield ...
...
if(runWithYields) yield ...
...
yield return null;
// coroutines need at least 1 yield or you get yelled at
}
Passing bools always confuses me, so I decided false was the “run the funny way” version. You’d still have to use StartCoroutine, but you could write a driver to hide it void myCoroutineInstant() { StartCoroutine(myCoroutine(false));.
Would recommend not to do that because at some point you will forget that this is still a coroutine, call it directly and then wonder why it doesn’t do anything ><
Been there, done that…
I have this coroutine with two parameters. I have attached this script on the Object but it doesn’t do anything. How can I call this coroutine in Update() method?
public IEnumerator Curve(Vector3 start, Vector2 target)
{
float timePassed = 0f;
Vector2 end = target;
while (timePassed < duration)
{
timePassed += Time.deltaTime;
float linearT = timePassed / duration;
float heightT = curve.Evaluate(linearT);
float height = Mathf.Lerp(0f, heightY, heightT); // change 3 to however tall you want the arc to be
transform.position = Vector2.Lerp(start, end, linearT) + new Vector2(0f, height);
yield return null;
}
}
In this clear case where you have just one yield statement, I’d extract the core of this method (the inside of the while loop up until the yield) into a dedicated normal method and just reuse that in both places.
That’s simply the usual way to call a coroutine regardless of where from: Update(), LateUpdate(), Start(), wherever.
This thread is about how to call a coroutine as a normal method instead. Which in your case, would be something along these lines as DragonCoder suggested:
// declare and initialize timePassed and end.
//Could also initialize them in Awake(), for example.
float timePassed = 0f;
Vector2 end = target;
void Update()
{
UpdateCurve();
}
void UpdateCurve()
{
timePassed += Time.deltaTime;
float linearT = timePassed / duration;
float heightT = curve.Evaluate(linearT);
float height = Mathf.Lerp(0f, heightY, heightT);
transform.position = Vector2.Lerp(start, end, linearT) + new Vector2(0f, height);
}
public IEnumerator Curve(Vector3 start, Vector2 target)
{
timePassed = 0f;
end = target;
// reuse UpdateCurve() in the coroutine as well.
while (timePassed < duration)
{
UpdateCurve();
yield return null;
}
}
A more general approach is to update the coroutine yourself manually using IEnumerator’s MoveNext() method. This doesn’t require to extract the “core“ of the coroutine as a separate method, and allows for any number of yield statements inside of the coroutine:
IEnumerator coroutine;
bool done;
void Awake()
{
coroutine = Curve(myStart, myTarget);
}
void Update()
{
// if not done yet, advance to the next yield instruction:
if (!done)
done = !coroutine.MoveNext();
}
Or, if you want to wrap the coroutine in a method to instantly run it to the very end instead of advancing it step by step: