Most (if not all) of the features are coded inside their own ActorComponents. This make things more modular.
The only thing you need to care about is initializing all the components. Once they are initialized, everything will work as expected.
You can see examples of such initializations inside the Enemy, TopDownRPGPlayerController or TopDownRPGCharacter classes. Check the init() function.
throughout the code, you'll see functions like PreInit, Init, InitOnServer, initOnClient ...
In V2, PreInit and Init are the two exposed functions which allows you to initialize your components. I simplified this from the previous version where you had to take care of calling the different versions on server and client.
If you go to the player controller, in the Init() function, you'll see those PreInitComponents() and InitComponents() functions which take care of the initialization of all the components.
Components can be placed inside the Controller or inside the Character. It's up to you to choose the location where you want to put them. There are few exceptions (AnimationComponent and TargetComponent) which need to be attached to the Character because they need to be present in all the clients during multiplayer game sessions.
For the player, I've personaly chose to attach most of the components to the TopDownPlayerController. For the Enemy, I've attached most of the components directly to the Character. (One of the reason is that you may want to change the player Pawn at runtime. Let say your knight is transformed to a wherewolf... Wherease transforming enemies is more rare).
The player is initialized in two phases :
Firstly, the TopDownRPGPlayer Character will be possessed by the TopDownRPGPlayerController. When the possession happens, we spawn the Proxies and we set references to these proxies. These references are Rep_notified and here is the rep_notify function associated with them :
You can see that when all the needed variables are setup (in the Rep_notify of OwnedCharacterProxy we check if OwnedCharacter is initialized and vice-versa) we call the Init() function which will begin the second phase of initialization.
In the second phase, we use the Init() function to initialize all the components :
As you can see, We call three different functions : PreInit(), InitServerSide() and InitClientSide(). Inside these functions we will called the PreInit(), InitServerSide() and InitClientSide() functions of each component.
- PreInit() is called both on client and server, before InitServerSide() and InitClientSide(). It is the perfect place for early initialization like initilizing Inventory slots for example.
- InitServerSide() is called on server. You can use it to initialize all the Gameplay related code.
- InitClientSide() is called only on the client. You can use it to initialize everything which is related to the UI.