Best practices for logic (player controller, pawn other)

As background I have spend about 6 months in blueprint learning the classes, then switched to cpp to start building away. I have a functioning “game” based on a few multi-player tutorials. Basic AI in waves, inventory system, pick ups, doors, platforms, etc. I started working on the UI side with Widgets/Slate. This is where things started to get messy. For reference:

Game Mode has:
all the databases (valid items, players, maps, etc) stuff i want only on the server
spawns AI

Player Controller has:
Inventory system
Weapon System (tied to inventory system)
all Menu Menus (this feels like a mistake)
Key binds for inventory, etc

movement and fire logic (line trace, etc)
health system

Now I find myself doing a lot of cross class setting so the hud is not displayed over the menu, the AI doesn’t start spawning until i initiate the active game via the menu, etc. Causing me to create a bunch of cache references and it is starting to feel like too much conditions. So what guidelines do others use? I have been toying with game instance, but it doesn’t seem to fit well for me. I am debating multiple game modes ( one for while i am on menus, the other for the active game logic). Basically i need some sage wisdom from the community.

Your setup seems reasonable. Our projects are pure C++, or as close as possible, with very little blueprint use except the HUD widgets.

One issue - are you able to control more than one type of pawn/character? If so, might want to move inventory down to character.

Another - do you have a player controller state stack? We have one for stacking player states like Walking >> ExitMenu >> Paused >> CutScene >> etc. Each state determines what inputs do.

Finally, are you using a dedicated HUD class? Each map allows you to specify default - GameMode, HUD, PlayerController, and Character.

Having the menus on the player controller is fine, I would also suggest handling the HUD on it. You should use a dedicated HUD class like Jocko suggested and change it on the player controller via ClientSetHUD when the state changes, eg. when you possess a new pawn. Having all UI be centralized on one object (the player controller) has the advantage that it’s easier to keep track of when to hide the HUD because of the menu, when to show the cursor etc. Also I would strongly suggest to not make references from gameplay code to the Menu or HUD, only the other way around. If your AI is waiting for something you do in the menu, let the menu execute a function on the player controller (replicated to the server) which in turn changes a variable on the game mode for example. The AI asks the game mode about the current state and not the UI. In general you will have a lot more readable and maintainable code if you avoid too many cross references, your gameplay should work without the UI. In some cases you may also want to created “manager” classes instead of having crucial code be managed by the UI. For example you could have a manager object for your user settings, the UI is able to read and write the settings on it, you can register a delegate on it from your audio code to change the volume when the setting was changed for example.

Thanks for the feedback and suggestions. I was not leveraging a player controller stack, I will try and implement that as it seems reasonable. Currently I have a dedicated (single) HUD. I am trying to break the setup before I go too far in the development of the content. Basically trying to write the core as clean as possible first, then expand.

On the inventory system, my logic was that the inventory would be persistent between pawn poses. I was trying to separate things that I believe are “player”/user id centric - what the player has collected, player level/experience, any virtual currencies earned/purchased. I was thinking the character pawn was a temporary state but the player controller represented everything I want to persistent between sign-on sessions.

I think I will move the HUD up to the player controller. Thanks for the well thought out responses.

Change parent class of HUD, Pawn or Controller… Or remove any of them from the code;
If for whatever reason your Blueprints get compilation errors then you’re doing it wrong.

In game development modularity is key, never make everything hard-reference everything else, work on game systems in isolation and make sure they work in their own self-contained environments.