- Home /
How to get a variable out of a coroutine?
I am using a coroutine in such a way where I need to know the value of a variable inside of the coroutine from an Update loop. Coroutines say that I cannot use Out or Ref variables in declarations. And I can't figure out how to get a coroutine to return it's value. I assume none of those things can be done.
I have the coroutine working the way I want it to by having the coroutine modify a global variable, but I really need to take advantage of the fact the coroutine is instanced on call. Meaning, I am going to be calling this coroutine multiple times in an update, and each instance of the coroutine is going to be modifying a different global variable. And I can't figure out how to get that to work, because if I pass my global variable into the coroutine through the coroutine call, the global variable gets instanced, and the actual global doesn't get modified by the coroutine.
How can I make it so that I can call a coroutine and have the coroutine modify different global variables based on how I call the coroutine?
It has nothing to do with the fact that it is a coroutine. The yield
keyword is reserved in C#because it is used in an enumerator to loop through a generic collection and it wouldn't make sense to to return a value besides for the element in the collection.
Surely if you pass an object to your coroutine instance, that object can then be modified by that instance:
yield FlashColorOf(gameobject1.renderer);
yield FlashColorOf(gameobject2.renderer);
This is providing "output" from the co-routine. How you access outside will depend on the rest of your code.
Rects are a struct and not a class, and structs are value types which is why it instances it. You could wrap the rect in a class that would update the members, using C# Properties, or you could pass whatever object currently holds said rect, like you'd do with GameObjects and Transforms.
Answer by Senhor de todo o Mal · May 23, 2011 at 01:22 PM
Like Peter G mentioned in his comment you best bet is to use a callback, here's an example:
using UnityEngine;
using System;
using System.Collections;
public class CoroutineTest : MonoBehaviour {
public Rect rect;
public void setRect(Rect newRect){
rect = newRect;
}
private IEnumerator leCoroutine(Action<Rect> rectCallback){
Rect newRect = new Rect();
//calculate new rect
rectCallback(newRect);
yield return null;
}
void Start(){
StartCoroutine(leCoroutine(setRect));
}
}
Use lambads to avoid having write setRect method: StartCoroutine(leCoroutine(result => rect = result));
And the last yield should not be yield return null;
but yield break;
, if anything at all.
Thank you for this example, this helped me alot I did not know this existed in c#.
Answer by Peter G · May 23, 2011 at 06:18 PM
You could put your rects in a container class that is passed by reference:
[System.Serializable]
class Container {
public Rect rectPassedByRef;
}
public class CoroutineTest : MonoBehaviour {
public Container myContainer;
public void Start () {
Debug.Log(myContainer.rectPassedByRef);
//prints 0 , 0 , 0 , 0
StartCoroutine ( MyCoroutine(myContainer) );
Debug.Log(myContainer.rectPassedByRef);
//prints 10 , 10 , 5 , 5
}
public IEnumerator MyCoroutine (Container HoldingObj) {
while ( someCondition ) { //The point is that it loops for while.
HoldingObj.rectPassedByRef = new Rect( 10 , 10 , 5 , 5 );
//Container will be passed by reference.
yield return null;
}
}
}
It isn't the most elegant solution, but it should work. According to MSDN though:
When you pass a reference-type parameter by value, it is possible to change the data pointed to by the reference, such as the value of a class member. However, you cannot change the value of the reference itself;
You are probably familiar with this, but its worth noting. Assuming my interpretation is correct, the reference is instanced. So you can still affect the edit object because the original variable and the local variable will be pointing toward the same object. But if you change what you are pointing at (There are plenty of ways to do this assigning it to a different reference or creating a new instance), then the original reference will not point at anything else. This is called passing a reference by value.
I see so class and objects outside the scope of the class will get referenced and variables inside the class get instantiated. Thank you for the example.
I got another slightly off-topic question. Does instantiating a coroutine count as allocation of memory? $$anonymous$$eaning, should it be avoided on iOS application to $$anonymous$$imize memory leaks?
If I remember correctly, no they don't allocate any memory. Starting the coroutine itself doesn't at least. Some of the return objects do such as yield return new WaitForSeconds(float time)
you are creating a new object so that will probably allocate memory.
The coroutine itself should not be a problem, but the code that's in it might. Check out this blog post on optimising coroutine yielding in C#: http://angryant.com/general/tipsandtricks/optimising-coroutine-yielding-in-c/ If you search around on the web there might be more on the subject, or you can just post another question here or on the forums. :P
A container class is a great approach. I enclose a variable in an array, which is how we wrote pointers back in the Fortran days. Ins$$anonymous$$d of this:
float v = 1;
yield return StartCoroutine(Some_Coroutine(ref v));
write this:
float[] v = new float[] { 1 };
yield return StartCoroutine(Some_Coroutine(v));
Answer by roman_sedition · Jun 12, 2021 at 12:03 PM
Sorry to necro this thread, but these days it might also be valid to use an async Task with await
Your answer
Follow this Question
Related Questions
How Return Or Restart A Coroutine When A Variable Increase? 0 Answers
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
How Return Or Restart A Coroutine When A Variable Increase? 1 Answer
can't read updated variable 2 Answers