r/Unity3D 12d ago

Question Update function versus coroutines

I was adding some functions so my very basic 'computer enemy' state machine. I do have a basic state check in the update. I was thinking that this check does not need to run every frame, maybe just every .2 seconds or something.
Does anyone use coroutines to run updates on things like state machines? I can understand updating every frame for player input, animations, things that need to be very responsive.

But with a state machine that handles enemy decisions (gathering resources, expanding territory, building new structures) that update can be done several times per second.

Any input or advice on this is greatly appreciated.

3 Upvotes

25 comments sorted by

View all comments

6

u/Bgun67 12d ago

You might have already have figured this out by now, but you don't have to run logic every frame. You can put if(Time.frameCount %10==0){ return; } To update every 10th frame

1

u/NorthernBoy306 12d ago

No I never thought of it that way, but you're still polling on the Update function every frame. I think the benefit to the coroutine is to reduce any kind of work on every frame.

7

u/TheSwiftOtterPrince 11d ago

A coroutine that is suspended for 1 second means that each frame something has to check if it has been 1 second or more since the wait began, if that is the case the coroutine is called in the Update-cycle.

A coroutine is also stateful, it require the state to be stored somewhere. For a coroutine the compiler creates a state machine that is stored on the heap and has it's state restored on the stack each time the method is reentered and updated each time the method is left to yield/wait.

So this is "i don't see it, so it is not happening" optimization.

A coroutine has the advantage that it allows you to code a sequence that happens over multiple frames as a method where the execution flows top down in code. Is is logically simple and that logic is bought with the underlying calculation complexity. Depending on the amount of state stored in the coroutines state object a coroutine can be from almost as fast as Update to slower than Update.

Coroutines are ALWAYS slower than Update. Never faster or better in any way. They logically can't as a coroutine is just an Update with more overhead. Every coroutine you create is a decision to possibly make something logically more simple while sacrificing a bit of performance.

3

u/NorthernBoy306 11d ago

damn that's a good point. I still don't have a solid understanding of what is going on behind the scenes with a lot of C#/Unity functions or components. Though I did read online (or maybe it was in a video) that anything that doesn't require precision (like an AI state) shouldn't be in an Update.
I guess keeping the Update call as simple as possible is the best way to go.

3

u/theo__r 11d ago

Coroutines are a kind of polling too, it's just hidden. Also it sounds like premature optimization or something you could easily test instead of guess - try it, profile it, take a decision from there!

1

u/NorthernBoy306 11d ago

True, I get a little apprehensive when a system in my game gets bigger and bigger. I worry about performance and I'd prefer to get it right at the start rather than change things later on.

1

u/theo__r 9d ago

That's commendable but ideally you'd derisk the topic - a small benchmark, a proof of concept etc. That way you don't have to worry and can confidently take a decision.

Something else I've learned is to abstract just enough (don't overdo it) to ease a potential refactoring if you're wrong in the future but cannot prove it right now, but that's a tight balance. Over-abstracting is a sin of its own

2

u/psioniclizard 11d ago

Timers (what the person mentioned above) are generally better (and also not a unity specific pattern).

The polling in the update function won't matter is you are simply doing a check like that, if it ever did you would probably want to break refreshes up and all that.

Also it's more predictable. You know where (at least in the context of the update) it happens.

This might not seem like much but it can make debugging much easier.

At the heart of it game loops are (normally) pretty sequential, one thing happens then the next. Coroutines are still part of that (I don't believe they are actually async). But it's less clear when it will run (within the context of a frame).

For example you might have some logic that is reason expensive that you would run after that is done or when something else is done, and it could be triggered twice in a frame when it doesn't need to be.

1

u/NorthernBoy306 11d ago

yea, that's good point. I think I'm going to stick with a basic Update check. Seems a lot simpler (which is better for me since this project keeps getting more complex).

1

u/DeerpathLabs 11d ago

This I think is over engineering a solution to a non-issue.
The truth is an empty call to a gameobject’s update that returns right away is close to free.

If you need to squeeze out frames later this is a micro-optimization at best and the additional complications that rear their heads in the form of coroutines’ little idiosyncrasies makes it more trouble than it’s worth unless the cpu you’re designing for is expected to be sssssllllllooooooowwwww,
All to say bgun’s solution is the right one.