ForLoop is non-blocking, learn it the hard way.

So I’ve been doing my game and pretty much didn’t visit forum for quite sometime.(got a shinny badge, cool)

First thing I come back is to share what I learn about ForLoop, it’s simple and yet could trick you if you are sort of veteran programmer. A ForLoop in blueprint flow control is totally different from a “for loop” in sense of say C/C++ or any scripting language that is. Traditionally, the loop body will run, complete(or into continue/break), before running next iteration.

(NOTE: Please correct me if I’m wrong.)
But in UE4, ForLoop is just there for rapid execution of events, with no checks at all if your loop body have any sort of dependency from “previous” execution.
In document, ForLoop fires loop body each frame, so it specifically have a warning sign tell you to not run long running loop or loop body to prevent undesirable result.(like loop through and emit 1000 missiles and expect it to happen next frame). What it didn’t say is, ForLoop does not block, at all, the next loop body or finish condition to run. The only thing for certain is that all the event will fire in sequence and per frame, but whatever follows is their own thing.

The most common one is, for loop with delay, like say, spawn enemy with a counter and randomly delay it a bit so it seems coming in waves. But, what happens if you try to use delay node before or after Spawn node with for loop? It will just spawn as soon as possible, frame by frame. This is a simple case and easy to work around it, you basically just need to take a look at Blueprint Communication level in ContentExamples, where on area you can hold to fire projectile(fire/water balls). It uses a timer function that binds to call next fire event repeatedly. Simple, easy and precise. You can also use a timeline in similar fashion if you want to drive (say animated spawning effect) before actual spawn happens.)

Now what’s the big deal about this? Here comes the crucial part, it’s not thread safe.

By saying it’s not thread safe maybe describe it in a wrong way, but result is similar.(note, it’s not UE4 itself running in a none thread safe manner) I have no solid proof to demonstrate this, but it will bound to haunt someone for days if you thought for loop would behave like a traditional loop. Imagine this, when a for loop is running, loop body have something that depends on previous result(ie. like the Fibonacci number loop). Your loop body run time might be different with branches and conditions, even additional event firing. Because for loop is non-blocking, your branch might fail, your isvalid might checking on a reference not yet assigned to actor spawned, the list continues. On top of all this, the complete pin is fired when the loop body finished firing and iterator is running to the end. It, however, does not mean all your loop body is current all finished!!

I didn’t see this brought up when I search for it, and I learn it the hard way and figure it out while debugging my game. Just to share this here so fellow game dev can have this drilled into your head when trying to run a for loop with any sort of between loop dependency, DON’T.

It would be better if Epic staff can clear this one up in the document and make it a common knowledge when people using blueprint to design their game.

Cheers~

Yeah, the ForLoop is so gimpy and easily broken that I’ve long ago given up using it (or trusting any other loop type BPs) and usually make my own loops. It’s a shame, but that’s the way it is, I guess.

ForLoop works more or less exactly how you’d expect (at least how I expected), in that it’s still a single function call which means it must complete BEFORE the frame is allowed to update.

You can’t use it for things that are spread out across multiple frames. It’s meant to perform the same sequence of actions on an arbitrary number of operands (i.e. perform this adjustment to every point of a given Spline, regardless of how many points it has) as a single execution. If you want to repeat something every X seconds you use a timer.

It seems to me that it’s only confusing to people whose expectation ISN’T that a frame will wait to update until all called functions/operations are executed. If you go in understanding that every execution path that is fired must complete BEFORE the frame is allowed to update, it makes more sense