Is Using "Delay Until Next Tick" to Make a Pseudo-EventTick Bad Practice?

When the player holds a button down, I want to run some logic every tick.

The normal way would involve saving a bool that determines if the player has the button held down or not, and running some logic in the Event Tick depending on if the bool is true.

However, I’m wondering if there’s any difference between using Event Tick, or the way I have here:

In this setup, I’m running the logic, delaying until next tick, and then checking if the bool is still set to true. It’ll keep doing it until the player releases the key, and FocusingFlashlight gets set to false.

This works exactly how I imagined it would, and feels immensely cleaner to me since it isn’t contributing to an ever-growing Event Tick sequence.

In fact, this might even be ever-so-slightly more optimal, because Event Tick would have to do the FocusingFlashlight check on every tick, whereas this setup only does it while the player holds the button down!

It feels too good to be true, though. Would anyone be able to tell me if there’s a catch to this setup? Something fundamentally wrong such that I should avoid it at all costs?

1 Like

Experimenting a bit more, this setup also does exactly what I want:

It runs the logic every frame while the player has the button held down, and stops when the player releases the button, without needing a variable.

Something feels wrong about it though, like I’m cheating the engine in some unconventional way to achieve what I want.

Also, I understand there’s probably always a way to avoid using Event Tick to achieve some functionality in some better way. However, this is a tiny game being made for a gamejam, so I’m completely fine with using Event Tick in general to get the game done faster. I just want to know if this weird Delay Until Next Tick stuff is really bad compared to using Event Tick.

Is enhanced input an option for your project? Might help solve this for you.

I’d never heard about the enhanced input plugin before. It looks cool, and like it’d work for this specific use-case I have.

However, I’m still curious in general if the Delay Until Next Tick loop I made earlier is any better or worse than putting stuff in Event Tick.

I did some very basic performance tests, just to sate my curiosity on the topic:

I created three different actors that all do the same logic every tick: they add DeltaSeconds to a float until it hits 1, and then subtract DeltaSeconds from the float until it hits 0, and then starts adding again, and so on.

The first utilizes conventional Event Tick logic, the second utilizes a DelayUntilNextTick loop, and the third utilizes Gates. All of these blueprint setups can be viewed under the below dividers.

Normal Event Tick Loop

DelayUntilNextTick Loop

DelayUntilNextTick Loop Using Gates

This one was hard to get all in one screenshot in a clear way, so just trust me that the logic works. While being less clear, the benefit to this one is that it doesn’t require a “Growing” bool to determine if the tick should be adding or subtracting from the float

The test involved spawning 10000 of one type of actor, and letting the game run for a minute with ‘stat GPU’ enabled. Here are the results:

  1. No Actors Spawned
    FPS: ~120
    Average MS: 5.82

  2. 10000 Event Tick Actors Spawned
    FPS: ~25
    Average MS: 14.79

  3. 10000 DelayUntilNextTick Loop Actors Spawned
    FPS: ~27
    Average MS: 15.33

  4. 10000 DelayUntilNextTick Loop With Gate Actors Spawned
    FPS: ~15
    Average MS: 16.09

NOTE: I don’t really know what I’m doing, because I’ve never done any formal UE profiling before, haha. Maybe I should be doing CPU profiling instead of GPU profiling, but I didn’t want to spend too much time researching this topic for what was supposed to be a quick little investigation.

With that in mind, the results were kinda inconsistent, likely because I’m not doing something right. Sometimes my FPS was 16 when the test starts and never changes, sometimes it’s 25 when it starts, sometimes it’s 20… the point is that it never really changed from the starting FPS when the game started running.

Similarly, the ‘stat GPU’ average MS value behaved fairly strangely. For all of the tests with actors, it would frequently start at about 6ms, and after some seemingly random amount of time (between 5 and 30 seconds), it’d increase up to 15-20ms or, and then stay there indefinitely.

One thing I can assert with 100% confidence, though, is that doing the Gate method had a noticeable impact on performance, as indicated by the FPS. While I do think it’s nice to not have to use a bool for conditionally running the tick logic, this performance decrease definitely makes me much more cautious about using it in the future.

I didn’t observe any evidence of using a non-gated DelayUntilNextTick loop causing any performance hits, though. Which is nice, but I’m still not sure if it’s bad practice to do so.

One last thing I was curious about is if there was a difference between an Event Tick that sequenced to 10 different functions, vs using 10 events that utilize a DelayUntilNextTick loop.

The logic in the following test is the same; every tick, I add the world delta seconds to a float value until it hits 1, and then subtract from it until it hits 0, infinitely. Here’s a screenshot of the logic:

Tick Logic

In the normal blueprint, I use a Sequence node to run the function that performs this logic 10 times, on 10 pairs of Float/Bool values.

In the DelayUntilNextLoop blueprint, I create 10 custom events that each host their own DelayUntilNextLoop tick loop that runs the same function on 10 pairs of Float/Bool values.

Once again, the node setups are under the dividers below. Sorry that they’re hard to see, it’s difficult to capture it all in one shot:

Normal EventTick to 10 Functions

10 DelayUntilNextTick Loops

Close-up of one loop:

And again, the test involved spawning one type of the actor, letting the game run, and observing the ‘stat GPU’ stuff. Here’s my results:

  1. 10000 Event Tick Actors Spawned
    FPS: ~40
    Average MS: 6.49

  2. 10000 DelayUntilNextTick Custom Event Loops Spawned
    FPS: ~24
    Average MS: 16.26

Again, maybe I’m not profiling the proper way, but I don’t think the FPS lies. I’d imagine the performance hit has more to do with how each actor allocates memory for 10 custom events than it has to do with the DelayUntilNextTick loops.

Regardless, much like everything else in software engineering, I don’t think it’d be bad to use these weird DelayUntilNextTick loops in moderation. It’s really nice in my main player character not having to shove all of the tick logic it runs into its Event Tick, and instead being able to put tick loops in the relevant areas of the blueprint when I need them.

Hope someone out there finds this stuff interesting :slight_smile:

What is your tick rate set to in the blueprint? If it’s 0.0 than for tests it probably overkill.
Try experimenting between values 0.1 and 0.5.

Does your focus logic have to run so many times per second?
Is it testing for overlapping actors with the flashlight cone?

Digging a bit in what DelayUntilToNextTick: it registers the function to be executed next frame and removes it after its been called. So by using delay in a frame by frame basis you are removing and adding a new reference every frame.

Pls someone correct me if im wrong: Tick works the same way, BUT the actor is already registered and simply gets called every frame. So for your tests it might be faster to just use Tick and enable/disable when needed.

1 Like

What is your tick rate set to in the blueprint?

In the tests I ran above, it was the default 0.0. I figured I wouldn’t want to change that in order to amplify whatever performance impact each method of loop-logic had on the engine.

Does your focus logic have to run so many times per second?

I suppose not. I think my question would still stand, though, if I replaced the “DelayUntilNextTick” node in my real project with a Delay set to 0.1s; if it’s bad practice to feed that Delay node back into the original branch statement, and to loop until the original condition is false.

Is it testing for overlapping actors with the flashlight cone?

All it’s doing right now is linetracing forward for WorldDynamic objects and printing the name of whatever’s hit.

I know I could probably do something more elegant with like… collision components detecting overlap events from other components or something, but for a project I intended to spend less than a week on, I was comfortable just doing some lazy tick logic to achieve what I wanted. My curiosities were moreso about if making self-contained node loop that feeds back into itself from a delay node was worse than using the Event Tick node.

Marking this as the solution for the time being, as this sounds hugely important for considering if self-contained delay loops are bad practice. Thank you for digging into it!

On a really high abstract level, my understanding of what you’re saying is that the engine creates a function for every Delay node that exists.

Then, whenever a node connected to the receiving connection pin of that delay node is executed, the engine has to make a note to “run” that Delay node function after X amount of time has passed (perhaps by storing a reference to that Delay function in a list, or something).

After X amount of time has passed, it then removes the Delay function reference from wherever it was stored by the engine, and continues down the execution path.

So in this example:

Every frame, this blueprint executes all three functions, and then adds a reference to the Delay functions to some list (in the engine, maybe they’re called Function 1234, 1235, and 1236, for example).

On the next frame, it then has to remove the Delay function references from said list, run all three functions again, and then add all of the Delay function references again, indefinitely.

This is all conjecture, so please correct me if I’m wrong! Though if my assessment is correct, then that definitely makes me more cautious about using these self-contained Delay loops in the future.