My Networking intuition is not there, I just need a quick clarification on this. Ultimately, I want certain gameplay variables to reside in the GameMode because it has a convenient method, PreInitializeComponents() ; which offers me a chance to initialize world scope variables that actors might depend on before BeginPlay is called on said actors. Alternatively, I could have used the GameState class because it encompasses a similar scope, but has no such method.
It has been made clear that GameMode exists only on the server; is the distinction totally irrelevant in Single Player games? I would imagine in a single player game, I AM the authority/server - hence using the GameMode should have no consequences at all and it should never be null?
If I cannot rely on it, then what alternatives do I have to initialize objects before begin play? Noting that these objects should exist on a per world basis, hence a GameInstance does not suffice.
EDIT: Additionally, is there a good reason why the GameMode has no access to the current UWorld - is it not safe to assume the UWorld is already instantiated by the time GameMode is alive?
If you are using GameMode to initialize global variables and want to make sure it’s done before any other Actors you should put that code in GameMode::InitGame.
Read more about GameMode and Actor LifeCyclefirst though.
In single player (UE’s definition is “Standalone”) you don’t need to pay any attention to RPC’s, Networking or Authority.
Game Modes do have access to the world (they’re actors) - the world is instantiated before the Mode, but PreInitializeComponents runs very early in the actor lifecycle (literally, before it’s components have been initialized). You probably want something a little later, like PostInitializeComponents().
Be aware that both Pre and Post initialize components also run when you open the actors blueprint for example, so be sure to guard against crashes you might cause:
if (GetWorld() && GetWorld()->IsGameWorld())
// Do world stuff
The key is to ensure my code is run before any other actor in the world calls begin play - some of these variables can be used by other actors on BeginPlay. Is it safe to assume GameMode is one of the first (if not the first) Actors to be instantiated; if that is the case, then technically I am free to select any of PreInitializeComponents(), PostInitializeComponents(), or InitGame() - is that correct?
World Settings is the first actor to be instantiated, I believe - and it is also responsible for calling Begin Play on all actors in the world. I think GameMode is indeed one of the earliest actors to be created though.
You can’t really ever guarantee the order of startup events to be honest. If you’re doing something that is time-sensitive, my vote would be to broadcast a delegate (maybe in the GameInstance?) when your Gamemode has finished setting things up. Actors can bind to that delegate and act accordingly.
Following on from what TheJamsh said, this might be of interest also…
There’s lots of start-up / timing quirks in general. Sometimes its better to avoid Begin-Play (or avoid making assumptions at least). In can often be better instead to use Do-Once nodes further down the event chain (if they are guaranteed to get called). Random example…
I redesigned aspects of the code such that the game instance initializes these variables now. While in theory it should work fine, it helps to know if there is a similar delegate that gets broadcast when the world gets unloaded or destroyed.
Fortunately, the objects I am managing are UObjects whose outer I set to be the UWorld being passed as an argument for PostLoadMapWithWorld - From what I understand, this means they will automatically be freed when the world gets destroyed so long as nothing outside the world references them.
There are delegates for AActor such as *OnDestroyed *or *OnEndPlay *that helps notify other systems the object is being destroyed. UObject doesn’t seem to have such a thing - I find that weird in cases where I want to design a managing class that might require some cleanup after a specific UObject gets destroyed. Hints at bad design on my end maybe?