I use GameInstance to hold some services and I thought it is constructed only once. But I see the GameInstance is actually constructed three times. Is this intended?
How should I approach this problem then? I want to initialize my services only once and I don’t like using singletons (but I will if there is not other way).
Being a UObject, GameInstance is constructed multiple times (CDO, Skel Blueprints, instances, etc.), this is normal. But you likely only have one instance being stored and used by the engine. In order to avoid unnecessary initialization on CDOs, you can check whether you’re dealing with a template object, i.e.:
Are you running this in the editor? I think it will get constructed by pie also. Maybe that’s your second one? Or you seeing two in a package also? I know one is for the default object but I think the code above takes care of that…
Well, at least we know it’s not the CDO, and that UGameEngine is constructing it. Engine Init is called in both FEngineLoop::PreInit and FEngineLoop::Init:
I don’t really know why. It’s also entirely likely that although the game instance is constructed twice, only one is kept. You could work around this by moving your initialization call to a separate function and manually call that once you know you have the game instance you want to keep.
Could be possible to do the initialization after the FEngineLoop::Init ? how to detect if the GameInstance is being constructed in preinit or init? (i would like to avoid dirty tricks like using a static count of initializations and similar weirdiness). The idea is to initialize the services as soon as possible, because they start async requests and stuff.
Of course I could do the initialization from my Game when it starts but it would be ideal to do this as soon as possible (ie in engine initialization).
GameSingleton might be a better candidate than GameInstance for what you’re trying to achieve here. It’s initialized even earlier, it is created even for non-game engines and I do believe it is created only once.
The GameInstance you want will be the second one being constructed since Init always follows PreInit. Odds are there’s some flag or state you can poll on GEngine to see which one it’s being constructed for. Try nosing around UEngine::Init and UGameEngine::Init, or maybe inspect GEngine in both cases using the debugger.