The very short version of the question:
Is there any additional function called in C++ that acts as a post-initialization and is more reliable than EventBeginPlay in that is it always called after every other setup function?
The detailed version:
Our team is new to Unreal, and we’ve been focusing on R&D for a little while to get up to speed on the engine. Specifically, we are learning the ins and outs UE4 networking best practices.
All of the gameplay concepts at runtime seem perfectly feasible and intuitive to us so far. However, we have been hitting a lot of road blocks in regards to network map travel and player login. There have been “solutions” to these issues that we can envision right away, but not only are they messy, but they seem to expose framework flaws in the engine that we are simply not comfortable accepting just yet (unless we’re doing something wrong of course).
I’ll focus on one issue in particular for now. EventBeginPlay.
It is our understanding that EventBeginPlay is intended to be the last function called just before ticking. In terms of engine design, we read that as “Everything should be fully initialized, and all setup functions should be called at this point.” That sounds like the only way an event such as this is useful. If there is even one case in which BeginPlay is called before a crucial initialization function, it pretty much invalidates the usefulness of the event. Here is one example we have come across that is leading us to believe the EventBeginPlay function is not safe:
Example: When using seamless travel with CopyProperties.
Let’s say we already have multiple players logged in to a lobby level. One is acting as the listen server, the others as clients. Each player can make a selection of what character they want to play, and are also assigned a player number (used for player colors). This information is stored in a simple struct that lives in the PlayerState for each user.
Our Goal:
When moving into the actual gameplay level from the lobby, we need the player choices to be carried over to the next level. We do this by taking advantage of the CopyProperties function that the PlayerState provides. Quite simply, when leaving the lobby to head to gameplay map, CopyProperties is called and we simply copy the struct to the new PlayerState. Then, when we reach the new level, EventBeginPlay should be called on the PlayerController, and we should safely be able to use that copied struct to spawn the correct player and choose the correct colors for the mesh.
The Actual Outcome:
The listen server will work perfectly fine. When travelling to the gameplay map, CopyProperties is called, then all of the EventBeginPlay’s start firing off. Great. The player spawns the right character, and gets the right colors. However, when each client comes along, for some reason, the function order is reversed! First, EventBeginPlay is called, then CopyProperties. Thus, they spawn the default character, and all have the same color due to having the default struct values.
We have confirmed that using the first tick to perform the same spawning “solves” this problem. But honestly, we’re not comfortable with that solution. All the code-smell alarms go off. What we really need is one event that is always called last, just before the first tick.
Is there any additional function called in C++ that acts as a post-initialization and is more reliable than EventBeginPlay?
If anyone is interested, we also have another similar example that highlights the EventBeginPlay issue in regards to PostLogin. But I’ll save that for another time.
Hopefully someone has come across this before and has found a more elegant solution than “DoOnce” in the first tick. Thank you for any help!