- Home /
How to pass "ref parameter" in iterator method?
Hi all,
I need to pass a "ref" parameter to a IEnumerator method, but I sadly found out that it's not possible to use "ref" as a parameter in a iterator...here's my code below, which of course throws an error:
public class CountDownTimer : MonoBehaviour {
public static IEnumerator CountDown (string objName, string displayEndText, float timer, ref bool boolToPass) {
// Create a new GameObject for the countdown timer and child it to "HUD"
GameObject countDownGO = new GameObject(objName);
countDownGO.transform.parent = GameObject.Find("HUD").transform;
// Create the GUIText component which displays the countdown and attach it to the countDownGO gameObject created above
GUIText countText = countDownGO.AddComponent<GUIText>();
countText.text = timer.ToString();
countText.font = (Font)Resources.Load("Fonts/Arial Black", typeof(Font));
countText.material = countText.font.material;
countText.anchor = TextAnchor.MiddleCenter;
countText.alignment = TextAlignment.Center;
countText.transform.position = new Vector3 (0.5f,0.5f,0.0f);
countText.lineSpacing = 1;
countText.tabSize = 0;
yield return new WaitForSeconds (1.0f); // Wait 1 second before starting the timer
// While the timer is bigger then 1, decrease it every seconds and round the number to an int
while (timer > 1) {
timer -= Time.deltaTime;
countText.text = Mathf.Round(timer).ToString();
// When timer reaches 1, wait 0.5 seconds, then display end text and call callback function, wait 1 second then destroy the gameObject
if (timer <= 1) {
yield return new WaitForSeconds (0.5f);
countText.text = displayEndText;
boolToPass = true;
yield return new WaitForSeconds (1.0f);
Destroy(countDownGO);
break;
}
yield return null;
}
} // End of CountDown IEnumerator function
I have looked around and found a few solutions, but I'm having a hard time understanding them and I haven't been able to successfully apply them to my own code. Could anyone show me how to do this with my code above?
Thanks a lot!
Stephane
modern solution here ...
Answer by Bunny83 · Mar 01, 2011 at 10:25 PM
How about using a delegate? I don't know in for what special case you need this bool but with a delegate you can pass a callback routine.
public delegate void OnTimerExpired();
public static IEnumerator CountDown (string objName, string displayEndText, float timer, OnTimerExpired callback) {
[...]
if (timer <= 1)
{
yield return new WaitForSeconds (0.5f);
countText.text = displayEndText;
if (callback != null)
callback();
[...] }
void MyCallback() { // Timer expired }
// To start the coroutine use: StartCoroutine(CountDown("...", "...", 5, MyCallback));
edit
You can also pass an object to your coroutine. Classes are always reference types. Just create a class that holds the data you want to return.
public class CCoroutineResult
{
public bool MyBool = false;
}
Define your coroutine like:
public static IEnumerator CountDown (string objName, string displayEndText, float timer, CCoroutineResult result)
You can easily change the bool inside your coroutine with: result.MyBool = true;
second edit
Well, just realised what you are doing inside your coroutine. You could go for another approach. StartCoroutine is a method of MonoBehaviour and the coroutine that is started is bound to the MonoBehaviour object. Since your function creates a temp GO you could run the coroutine on that gameobject. Just use a static method as a kind of constructor and remove the static from your coroutine. Create the GameObject within your static method and return the script instance.
public class CountDownTimer : MonoBehaviour
{
public string displayEndText;
public float timer;
bool Done = false;
private IEnumerator CountDown ()
{
// Here you can do all your stuff on the actual GameObject.
// Just refer to `gameObject`
[...]
}
public static CountDownTimer CreateTimer(float aDuration, string aEndText)
{
// Create a new GameObject for the countdown timer and child it to "HUD"
GameObject countDownGO = new GameObject(objName);
CountDownTimer CDScript = countDownGO.AddComponent< CountDownTimer >();
CDScript.timer = aDuration;
CDScript.displayEndText = aEndText;
CDScript.StartCoroutine(CountDown());
return CDScript;
}
}
Bunny83, thx a lot for your great answer, it helped me understand a few things I was confused about! I'll give it a try and see if I can make it work with your example :)
I do like your solution on using a class as it's always a reference type, but the boolean I need to change is defined within another script, on another game object, so I would first have to create an instance of the other script in order to access the class, which I don't want to do.
I created the CountDownTimer.cs class so that I can access it from any other script, and change the variables of these other scripts by passing them to the CountDown() method. So if I pass a boolean variable to CountDown(myBoolen) and switch myBoolean to "true" withing CountDown(), I want it to update whichever boolean variable was passed to true as well.
I do have a version of this script which uses a delegate, and works like a charm, but I wanted to try a version which lets me change boolean states on other script without having to pass a callback method...I hope I make sense?
I haven't tried your second edit yet, but I will because I think it might fix my problem.