OK to use recursive function with IEnumerator?

Hi,

I wrote a script that will play random animations attached to the same object (actually only 2 different animations at this point) and then pause for random seconds. I tried to do this using InvokeRepeating() but you can’t pass in parameters to the function you are repeating that way. Here’s what I came up with:

#pragma strict

private var windZ : GameObject;
private var treeWind : String;
private var treeWind_sm : String;
private var treeWindLength : float;
private var treeWind_smLength : float;

private var lengthOfBlow : float;
private var ranSecs : int;

function Start () {
	windZ = gameObject.Find("WindZone");
	windZ.active = true;
	
	treeWind = "TreeWind";
	treeWind_sm = "TreeWind_sm";
	
	treeWindLength = animation["TreeWind"].clip.length;
	treeWind_smLength = animation["TreeWind_sm"].clip.length;
	
	lengthOfBlow = treeWind_smLength;
	ranSecs = 5;
	DoWind();
}

function DoWind(): IEnumerator{// Don't know how the IEnumerator thing works -- just does
								// allows recursion -- console comments gave it to me
	if(lengthOfBlow == treeWindLength){
		windZ.animation.Play(treeWind);
		Debug.Log("treeWind");
		Debug.Log(ranSecs);
		yield WaitForSeconds(treeWindLength + ranSecs);
		
	}
	if(lengthOfBlow == treeWind_smLength){
		windZ.animation.Play(treeWind_sm);
		Debug.Log("treeWind_sm");
		Debug.Log(ranSecs);
		yield WaitForSeconds(treeWind_smLength + ranSecs);
	}
	
	var forMakingRandom : int = randomRangeInt(0, 2);
	switch(forMakingRandom){
		case 0:
		lengthOfBlow = treeWindLength;
		break;
		
		case 1:
		lengthOfBlow = treeWind_smLength;
		break;
	}
	ranSecs = randomRangeInt(5, 21);
	DoWind();
}

function randomRangeInt(min:int, max:int):int {
	var randomNumInt : int = Random.Range(min, max);
    return randomNumInt;    
}

Some error messages from the console gave me the hint to make the DoWind() function typed as IEnumerator and that would allow it to work as a recursive function – which it did. It works very well. What bothers me is that I don’t understand how it works or why typing a function a IEnumerator will magically allow it to work as a recursive function. Is there a better or “healthier” way to do this? Is the IEnumerator thing a permanent part of Unity/JavaScript? Is it likely/not likely to be deprecated in the future? Thanks.

Zaffer

IEnumerator is a core mono feature. It is the technology that coroutines (functions with yield in it) are built upon. Unity’s JavaScript tries to hide the technical details from the user, but apparently there are cases where it can no longer hide all the details from you. Any function with yield is always of type IEnumerator, it’s just that your specific use is complex enough that you’re forced to explicitly define it.

I’m not entirely sure how yield is implemented, but your recursion looks like it causes a memory leak when your coroutine runs for a very long time. It looks to me like yield has to store the current position in the method to be able to continue execution at this point when the next item from the enumerator is being requested.

With recursion your stack rapidly increases in size, especially if you pass a lot of parameters to the method or if your method creates a lot of local variables. Every recursive method call has some memory overhead on the stack, unless your compiler automatically optimizes tail recursion. So what happens if you make this recursion for a very long time? The memory overhead of every recursive call adds up.

I would prefer writing it as an infiinite loop, e.g. while (true) { … }

Hi Tomvds and Plzdiekthxbye,

Thanks for the clear explanations. I didn’t know about while(true). Looked it up and apparently it’s OK to set up an infinite loop as long as it has a yield statement in it – won’t crash. So I tucked my whole DoWind() function into while(true) {…} and it worked beautifully! Thanks for pointing me away from the “dark arts” of hidden core features.

Zaffer