[C#] Passing and Changing Variables Using A Coroutine

I’m developing and AI and I’m wanting to delay the decision making process, so I’m using a coroutine. It’s meant to check the distance of the Player, and edit an int to determine if it should move or not. The coroutine is supposed to edit a variable named “MoveZ” but whenever I check the value after the coroutine runs, it doesn’t change the value. Here’s my code:

Declaring the method

IEnumerator FindRun(int Z){

        if (Distance > 3){
           
            Z = 1;
           
        }
       
        else if (Distance < 2){
           
            Z = -1;
           
        }
       
        else{
       
            Travelling = false;
       
        }

        yield return new WaitForSeconds(.1f);
   
    } // Closes FindRun

In Update

StartCoroutine ("FindRun", MoveZ);

What am I doing wrong? I’m not sure what “return” does, does it have anything to do with that?

all the code in couroutine should go after “yield return new WaitForSeconds(.1f);”

-Edmond–

Thanks for the tip, but it doesn’t solve my problem x-x

Try using the non-string version of StartCoroutine:

StartCoroutine(FindRun(MoveZ));

I prefer to use the non-string version because it’s clearer about what’s happening. Plus, it allows you to use coroutines with more complex parameter lists than just the one.

If you want your coroutine to delay what it does, then you need to put the yield return line at the top of the function.

Ordinarily, in a function, as soon as you hit a ‘return’ statement, it’s over. Nothing after that line is ever executed. A coroutine is a little different. Once it hits that ‘yield return’ line, it DOES return, BUT it also marks that spot.

It returns an object called an IEnumerator which contains the current running state of the coroutine, including the line at which it stopped. When StartCoroutine gets that object, it stashes the IEnumerator in Unity’s “list of coroutines”. Once per frame, Unity runs through this entire list and kicks up each function where it left off, one at a time; if the function comes across another ‘yield return’ line, it again jumps out of the function, and Unity holds onto it in its queue of coroutines. That’s what happens when you use ‘yield return null;’ in a coroutine; effectively, it tells Unity to wait for one frame.

There are special commands you can give it when returning, the most common of which is ‘yield return new WaitForSeconds(x);’ This makes Unity, instead of tossing it on the ‘next frame’ queue, toss it on a different particular pile based on the command you gave it. This makes ‘yield return new WaitForSeconds(0.1f);’ do exactly what its name says, and at exactly the place where you put it.

The important takeaway here is that yield returns are special, and unlike normal returns, they don’t always come at the end of the function.

1 Like

Thanks for the clarifications over coroutines, I appreciate it greatly. This community is really helpful for the confused.
However, I still can’t figure out why my MoveZ isn’t being changed outside of the function. Whenever I use Debug.Log(MoveZ); it says “0” in the console where the values should have been “1” or “-1” and I can’t figure out why.

MoveZ will not change. It is passed by value and becomes the local copy called “Z,” which IS being changed.

If you want your MoveZ to change, you can either reference it explicitly from the coroutine (not a great way to go about it), or use a closure (functor) to allow the coroutine to modify it by callback. I’m not familiar with functor syntax in Unityscript, only C#, but there are plenty of examples out there. You are essentially creating a small anonymous function that is a block of code that takes the new value you want to put into MoveZ and sticks it into MoveZ, which ought to get you to where you want to be. You would pass this functor into your coroutine, which is yet another reason to use the non-string version.

1 Like

I’m unable to find much documentation over the closures, other than one tutorial which didn’t make too much sense to me, and used syntax I’ve never seen before. Is there a Unity tutorial or anything else for this?

Also, would there be anyway to avoid doing this in another fashion? I’m still learning how to code, so I’m not sure if I’m passing anything simpe up here.

Whoops, somehow I thought you were using javascript. Never mind. Yes, here’s the syntax snippet you’re looking for in C#…

IEnumerator FindRun( System.Action<int> callback){

        yield return new WaitForSeconds(.1f);
 
        if (Distance > 3){
         
            callback(1);
         
        }
     
        else if (Distance < 2){
         
            callback(-1);
         
        }
     
        else{
     
            Travelling = false;
     
        }

    } // Closes FindRun

// At your call site:

StartCoroutine ( FindRun( (i) => { MoveZ = i; } ) );

The closure is the little snippet “(i) => { MoveZ = i; }” blurb, which is a tiny little anonymous function that accepts a single variable of type int and assigns it to MoveZ in the call site’s context. Closures are neat and useful in many Coroutine and other semi-asynchronomous contexts.

8 Likes

I appreciate this. Some intimidating code though, hahaha. I’ll try to work this out. Thanks!

Yeah, I went through the same “why would I want to do that?!” phase with functors/closures, and then one day I saw the light. Coroutines can really be my friend now…

Be sure you grasp the actual functionality going on there. It’s sort of a lambda, but more because it can capture local variable context and state.

There are times when writing a particular thing with closures allows you to completely “turn the code inside-out,” and make it all much simpler and cleaner as you go.

1 Like

I’m confused by this “lamba syntax” I’m seeing everywhere, could you explain the

StartCoroutine ( FindRun( (i) => { MoveZ = i; } ) );

and the

IEnumerator FindRun( System.Action callback)

please? It’s kinda confusing, not a lot of documentation on it, it seems. At least, I cannot find it.

(i) => { MoveZ = i; } )

This is pretty much equivalent to this

void SomeMethod (int i) {
    MoveZ = i;
}

It’s often referred to as an anonymous function, because we haven’t given it an explicit name. Anonymous functions are generally used for light weight methods that won’t hang around for long. You could also use the named method if you liked, like this

StartCoroutine(FindRun(SomeMethod));

System.Action is shorthand for writing out a delegate. It’s entirely syntatic sugar. Read up on delegates to get the general concept.

2 Likes

Adding to @Kiwasi , another term for a delegate is a function pointer with a context, i.e., which object do you want this function to be called on. If it is static, then there is no object, just the static context of the class itself.

Functor syntax just saves you having to come up with different function names for each little function that you want to pass around like this, plus makes the code tidier because it keeps all the logic localized to the call site.

1 Like

Thanks a ton guys, you really helped with simplifying the logic of the code into understandable bits. Often when I look at examples and explanations, they use a lot of terms I don’t really understand, and when I look up those terms, I typically don’t get what I’m looking for, or they use more terms that I don’t understand. Don’t know if I’m the only one, lol, but thanks

1 Like

Definitely not. I frequently come here to ask for step by step instructions on a coding concept I couldn’t quite grasp.

Glad we could help.

1 Like

So, just a quick clarification, is this functor supposed to change the variable outside of the function? I’ve been fiddling with it, and it won’t do that. I have the same issue I had originally, once it leaves the function it drops the value

Edit: Now that I reread the code, I suppose it shouldn’t be changing the value outside of the function. How could I get it to do this?

Found any solution? I know this is an old thread but I am experiencing a similar problem and could not find clear explanation anywhere. The value being thrown from the Coroutine, even when passed through callback does not reflect outside that routine.

What is the problem you’re having? See enclosed project for fully-functioning example of my original 2015 post including scene and code.

3565777–287378–CallbackDemo.unitypackage (4.72 KB)

I’ve finally solved my problem. I was using the wrong variable name! I was trying to hijack a value being thrown from an open source script and change it in real-time. But before I got it, I tested your code and it worked like a charm. I had better understanding now on Coroutine callbacks. Thank you very much! :slight_smile:

1 Like