Is it possible to Tick an actor from outside the game thread?

DISCLAIMER: all of the following is based off of a few tests in blueprints. Do your own tests or look at the source code to verify this.

I did some more tests and I found out that that input always fires before the tick and in the same frame as the tick. Now, input being fired is not the same as input being received. However, if you notice: input always fires (1) before the tick, and (2) in the same frame (i.e. at the same game time) as the tick. This means that the tick always uses the results of the input. If an input is received after the tick begins, it is fired in the next frame and will use the results of the current tick. This ensures that the input & tick are performed in the same order that they happened (i.e. chronological order).

Visually, it would look like this (where each number in brackets is the frame number):

input[0] → tick[0] → input[1] → tick[1] → input[2] → tick[2] → …

Notice how every input has the same frame number as the tick after it. This is because ticks work on a single moment in time (which is why the game time stays constant through the entire frame). Any input that happens after this time cannot affect that tick because it chronologically occurred after the tick, so it is performed in the next frame before the next tick. Since input that occurs after the current tick is performed before the next tick, input is never actually “delayed” as this is the chronologically correct place to put it.

So what does this mean? Put simply: input is already handled the best way possible. It is performed at the chronologically correct time and is independent of framerate or frame time. You can test this by setting your framerate to 1 and performing any input; the action of the input (e.g. walking or camera movement) will always happen on the next frame in the correct order no matter when on the current frame you performed it.

There is a difference between Action and Axis inputs. Between input value and input events.

What is Important to me is axis type of inputs such as MouseX, MouseY, JoyStickX and Y. All of those are axis inputs meaning having values of (-1.0 to +1.0). My major focus is on those axis values, and the general input handling system as a whole as well.

What is a frame? what is a tick?
The tick is a function on a UObject that is called in runtime, once every frame. The frame is the period where all tickable UObjects get ticked (called) within the game loop. A tick can happen at the beginning, middle, of end of a frame depending on some ordering rules and options.

So when we talk about the input tick here, we are actually talking about a little function being called on the player controller which will handle calculations of inputs that are being collected since this same tick function was last called in the previous frame. There’s some nuances for each type of input, and multiple input sources pouring into one game-perceived input and whatnot that I have yet to fully know, but the concept is what I said specifically for float-valued axis inputs that are my concern ( and not buttons or input events).

So in terms of order of events, everything runs in well ordered lockstep fashion as you just noticed. This is quite clear and is a fantastic job by unreal game loop already.

Now before going back to our game frame, let us just have a look at the input controller itself. This is an input device that is being polled periodically by the operating system, let’s say at 120 times per second. The polling reading will then be stored in the RAM (or equivalently accessible OS source) for other applications to pick. The game is the application that will pick those readings.

Now say both the game and the device polling were at 120hz, and suppose that both are running with no fluctuations at all, which is far from reality, but let’s suppose that is the case. In this case the frame length is actually 8.33 milliseconds for both loops (the game loop and the polling loop). There is by definition a “delay” of input pick up by the game loop ranging from few nanoseconds to 8.32 milliseconds depending on the difference two loops starting points. There is no way around that. We have a delay, worse still, we don’t know how much exactly, and we don’t guarantee its steadiness either. Now is this delay actually noticeable by players? No, not at all. But can we utilize this delay for our advantage as game makers, yup and that’s my job here.

My concept is not only to accept the existence of this delay, but also to increase it by few milliseconds (say it will be 10 or 11 milliseconds) to include barely two pollings.

What do I gain, three main things:

  1. The delay will be virtually fixed with no fluctuations[stability]
  2. The delay will let me have two polling readings prior to applying input to game logic[valuable information].
  3. Game logic will run with precisely similar mathematical results (collision/velocities etc) at low fps just as at a high fps, so long the input picking thread is working properly on the two versions of the game.[this is just huge benefit]

The last point is related to the fact that the game has its own physics engine that already runs the same regardless of frame rate, and DeltaTime fluctuations.

All in all, this manufactured delay will be unnoticeable by the player, but measurable by the machine and extremely useful in our input handling arithmetics, animations logic, physics, and a bunch of other concerns.

I have started already studying UE4 input handling, it is really a cool system, but it will take a long time for me to fully analyze it and I just wanted to see if somebody here could help me out.

My hope it that I can find the a way through UE4 to deal with input without needing to go directly to the operating system, or any other low level input handling because I don’t want to lose UE4 systems of dealing with a ton of input sources simultaneously, I need either to hijack that system as is, or replicated. I really don’t want to end up making a plugin that might give me headache after game launch. I am a solo dev and I need guaranteed solutions,

Yeah, I just did the tests with the axis events and they don’t work like in my previous post :sweat_smile:. I did a test at 1 frame per second, pressed an axis event (move forward) multiple times, and it was completely missed as if it didn’t even happen. It only caught it if I held it down until the next frame because it only uses the value it is when it’s polled.

Do you mean that the simulation runs at a fixed time step?

1 Like

Yeah I am fully aware of that, but I meant further differences in lots of details inside the engine code handling each of them.

If by simulation you mean the game’s physics simulation, then no.

Ok, good.

Well, I think I’ve done all I can without digging too deep. I don’t know if you’ll be able to use engine only input data because your polls will be no different then what the engine polls. You’re gonna have to find a way to get OS input data, whether that be through engine functions or through a custom solution.

Though, now that I think about it, I don’t think you’ll get reliable results from polling in another thread. From my understanding of multithreading, just because something is in another thread doesn’t mean it’s constantly active, which, in your case, means you’re not promised a poll will happen at any specific time (i.e. there will be fluctuations in your polls).

1 Like

I’d like to just thank you for your help, such an amazing effort.

It’s just what I am looking for is a multifaceted challenge beyond the scope of one question in a forum.

When it comes to multithreading, there are prioritizing options and other details that might give me all I want, btw I’d be actually happy to sacrifice some of the game loop speed for the sake of input reliability. And you have to know that UE4 already picks the inputs extremely fast, it just doesn’t incorporate them into the game loop until the next frame start. Mainly because most games don’t even need or want that aggressive input gathering. Anyway, overall I think I am on the right path really. I just found few higher level abstraction classes outside of the game framework code, and some other stuff that look promising.

Nonetheless, thanks again, really appreciate all you’ve done since the beginning of this post :+1:

1 Like