How does the game loop actually work?

I see some function gets called every tick some actor don’t etc. How the the game run everything is my which main class are being called how it’s looping and etc?

Hello Kufi,

there is a bunch involved with the game loop. Here let me give you an overview:

All code related to how the engine starts up, its main function, and loops (ticks) can be found inside the Launch module (Source/Runtime/Launch). There you will find a file for each platform supported providing platform specific initialisation: the Windows environment uses a special Windows-only entry point, Linux the standard C++ one, Mac I didn’t work much with but I think it uses a custom OS main version as well. Ultimately they all call the GuardedMain function found in Launch.cpp which performs the engine’s ‘heavy’ initialisation and will at some point enter the engine loop until a termination request has been received (more about that loop below). There are other platforms, such as HTML, on which the GuardedMain is not called. This is because in HTML you must register a tick function which will be called by the environment itself; ultimately though, all approaches call GEngine.Tick().

In GuardedMain, after all initialisation code has been run the engine enters the engine loop. Here is an excerpt:



// before GuardedMain
void EngineTick( void )
{
    GEngineLoop.Tick();
}

// in GuardedMain function
while( !GIsRequestingExit )
{
    EngineTick();  
}



Just as a side note, it is quite interesting to look at all the initialisation code (there are still comments referring to bugs from UE3 haha) as it sets up all the framework it really helps understanding the engine (though I will leave that logic out of the scope of this post). Now to the engine loop itself:

The loop function itself is 400 lines of code long. In this paragraph I will describe everything that is done before Tick is called on all actors, etc (so in case you’re not interested you can skip this section). It starts out by sending a heartbeat to diagnostics threads (the profiler which can be used in the editor); looking at all the loop functionality, and Unreal code in general, a bunch of code is bloated with macros counting cycles spent in certain sections and sending that information to the profiler. After the heartbeat, the loop immediately checks whether any tickable objects in the rendering thread are allowed to be ticked ticking them if so; do note that this is only done if running in single threaded mode. The loop now proceeds by updating memory allocation statistics and checking whether the engine is in idle mode. In idle mode, the game thread yields its CPU time by sleeping. After the sleep, if slate is initialised it is provided with an user input. We are nearly at the point of ticking the level: it is now required to compute the delta time to be passed on to all tick functions. This is done in UEngine::UpdateTimeAndHandleMaxTickRate which will compute the delta time and possibly wait to enforce a maximum frame rate. Now finally, the super duper tick function that causes AActor::Tick and others to be called is reached (described in next paragraph). Importantly note that only after the first time the engine calls tick, the loop function will wait for the start-up movies to finish playing; these are the movies that can be configured in Project Settings → Movies. This waiting is done in IGameMovePlayer::WaitForMovieToFinish (it literally waits using a while loop and does not exit the function for quite some time - slate continues to be ticked in said while loop). You might wonder why the engine does not simply wait for movies to play before it bothers ticking anything at all: this is because that is where user code would initiate a load / movie. Only after the movies have been played, the engine tries to compile any ready asynchronous shaders which I do not understand right now because I personally think it might be a good idea to compile shaders in the background while playing the movies; there is probably a very good reason behind it though which I have not stumbled across, yet. Those sneaky Unreal developers are quite smart.

Now to the super duper tick function I mentioned earlier: UEngine::Tick. First of all, there are three implementations of tick depending on your build mode: UEngine is just the base class and the sub-classes UEditorEngine, UGameEngine and UUnrealEdEngine provide definitions, respectively. I ASSUME UGameEngine is what is run in a packaged game but I did not actually investigate this. I will be describing UGameEngine as that is really the one we are interested in. UEngine::Tick first does a lot of updating of the game viewport: closed viewports are cleaned up and if there are no more viewports left, a application shutdown is requested (remember the while loop above that has GIsRequestingExit? this will be set to true ending the engine loop after the actual frame). The first actual tick function called is StaticTick on all UObjects which wish so. Afterwards, the analytics engine is ticked. We are finally getting closer to our goal: now the engine will proceed ticking all worlds by iterating the list of active worlds. If this game is not a dedicated server nor a commandlet, the sky light following reflection captures are updated. If the engine is not in idle mode, the world itself will be ticked using UWorld::Tick. After this call to UWorld::Tick, there is a bunch of adminstrative code that is run after the first tick only: level streaming updates, etc. which I will not discuss. In UWorld::Tick: The spawn actor counter is updated, if the engine was compiled using the collision analyser that will be ticked, and the network code for the client is now ticked. An important detail to note is that the delta time that was passed to UWorld::Tick is used for those purposes, which is real world physical time. After those tasks, the delta time is updated using the world’s time dialation. This is very important to note because it would be fatal if the networking drivers received a time dialated value as that is a real-time system. If there is a seamless travel going on at this stage, more of the CPU’s time slice is given to load that travel. Now comes the moment everybody has been waiting for: finally all actor and component ticks are called using UWorld::RunTickGroup. Another note is that the tick group are now run in order as described here. Here let me post the code of that section:



// If caller wants time update only, or we are paused, skip the rest.
if (bDoingActorTicks)
{
    // Actually tick actors now that context is set up
    SetupPhysicsTickFunctions(DeltaSeconds);
    TickGroup = TG_PrePhysics; // reset this to the start tick group
    FTickTaskManagerInterface::Get().StartFrame(this, DeltaSeconds, TickType, LevelsToTick);

    SCOPE_CYCLE_COUNTER(STAT_TickTime);
    {
        SCOPE_CYCLE_COUNTER(STAT_TG_PrePhysics);
        RunTickGroup(TG_PrePhysics);
    }
    bInTick = false;
    EnsureCollisionTreeIsBuilt();
    bInTick = true;
    {
        SCOPE_CYCLE_COUNTER(STAT_TG_StartPhysics);
        RunTickGroup(TG_StartPhysics); 
    }
    {
        SCOPE_CYCLE_COUNTER(STAT_TG_DuringPhysics);
        RunTickGroup(TG_DuringPhysics, false); // No wait here, we should run until idle though. We don't care if all of the async ticks are done before we start running post-phys stuff
    }
    TickGroup = TG_EndPhysics; // set this here so the current tick group is correct during collision notifies, though I am not sure it matters. 'cause of the false up there^^^
    {
        SCOPE_CYCLE_COUNTER(STAT_TG_EndPhysics);
        RunTickGroup(TG_EndPhysics);
    }
    {
        SCOPE_CYCLE_COUNTER(STAT_TG_PostPhysics);
        RunTickGroup(TG_PostPhysics);
    }
	
}
else if( bIsPaused )
{
    FTickTaskManagerInterface::Get().RunPauseFrame(this, DeltaSeconds, LEVELTICK_PauseTick, LevelsToTick);
}


As you can see, if the game is paused using APlayerController::SetPause(bool), another number of tick functions is called instead. I did not look down what is actually run then so I will not comment on it right now.

Now that the world has been ticked in UWorld::Tick the call stack unwraps back to UGameEngine::Tick, where all instances of FTickableGameObject are now going to be ticked; if you are wondering how the engine obtains all instances, it is quite simple: There is a static array of instances declared inside FTickableGameObject and the default constructor adds itself to said array and its destructor removes itself from it again.

There you go haha.

Cheers,
Univise

6 Likes

Great reply,I’ll be sure to always refer to this, thank you. The unreal engine folks don’t load the shaders in movie due to the fact many developers use all the resources during a load movie scence/aka cut scene.

Also I see i need practice scoping through source files.

The debugger helped a lot figuring this out. The easiest way is to create a custom actor with a tick function adding a break point to the beginning of the function; using the debugger one can easily see the call stack. Alternately, you can also perform an "informed’ crash by dereferencing a null-pointer which will also yield the call stack in the crash reporter; the call stack looks much nicer in that reporter than in the debugger haha.

Is there something you are looking at in particular?

Yeah i think I recalled that everyclass atleast gets called once per cycle? Like if it 120 frames unless it’s in some tick or special function it just gets called once every 120fps or 60fps etc etc. And is 4.15 a build I’m supposed to be using I’m running into alotttt of bugs… Where I think it’s my called but not and just have to reset the engine

Yes, 4.15 is the version to be on.

I am not aware of a function that is called once a fixed frame rate has been completed. Every tickable actor’s tick function will be called every frame. So if you are following my suggestion above just spawn that actor in the level once and you are ready to go.

Thanks, I run into engine errors and question my code, now I see how to conduct a proper work flow.

I believe my the proper way of asking that question is. If I diasble tick for that actor how many times will the engine iterate through my code after that? After the second which I’ll call the set cycle or etc.