Multiple Inheritance and UE4? AI and PlayerController

I have an AI Controller and a Player Controller with some identical logic…and was wondering if there are any pitfalls into putting them in a common class and having them inherit them both. Issue is, because AI and PlayerController have different parent classes, it would need to be a second inheritance, so multiple inheritance. I didn’t want to do this to find out later there is some sort of limitation with UE4 and multiple inheritance, was wondering if anyone else has done this and if there is anything to look out for and how the Unreal Build tool might handle this and casting.

Yes, there’s a limitation with UE4 and multiple inheritance: It can’t be done.

When dealing with UObjects, you can only use single inheritance. The most likely reason is the myriad complications that stem from reflection and RTTI on multiple inheritance. If you’ve heard people complain that dynamic_cast and typeinfo are awful slow, multiple inheritance is why. Epic chose to ignore it altogether by limiting to single inheritance for uobjects. However, you can use UInterfaces. But being interfaces, you can’t actually provide common implementations, only shared definitions.

But UE4 has this wonderful little thing called Composition, through ActorComponents. If you have logic that is truly shared between both controller types (and not character types, as things tend to be in a “proper” design), then you can just create a component and plug it on both AI and Player controllers.

3 Likes

The logic is mostly identical, with the main difference just being, the AI portion which just calls those common functions. Thanks for the answer, I may very well move things into a component then, atm had just been essentially copying them , thinking that later there may need to be special differences for AI anyways but that hasn’t materialized, and its becoming a pain to insure they are both in the same state. ATM I am using an interface, but like you said, I can’t really put implementation there.

What are you sharing between both controller types? A common mistake is to store character state and functionality in controllers, when it should be stored on characters/pawn.

My game is a little complicated in this regard. Its an RPG, and the ‘Pawn’ is just an image, with no animation, and can not move, no collisions, essentially the game is completely controlled through a set of options. As such, I’ve put alot of my logic inside the Player Controller, because the player doesn’t really control a ‘character’ anyways. The player selects an option in the UMG menu, and that input is passed to the player controller, where most of the magic happens (logically). Events are thrown, the pawn and menu listen to them for certain visual effects to occur. While I could have found a way to force some of this logic into pawns, it would have made my logic alot more complicated then it needed to be.

PlayerController - Brain.
Pawn - Body.

Don’t try to force everything into PC, because you actually make your design more complicated than it need to be ;).

The rule of thumb, is that PlayerController should contain informations which whouls persist after pawn is dead/switched for the duration of single game, and which does not need to be replicated to other players (since PC is in replicated to owner).

Pawn should contain information, which are specific to single pawn (like attributes, health, current weapons, etc). and which are need to be seen by other players.
Sometimes you want to have certain persistance (pawn dies, it respawn with certain items/attributes). You can pull this, of by plain serializing current pawn state when player saves game, story temporary pawn state somwhere in memory where it will persist (player controller, game state, game instance), or just loading defaults.

Anyway. Player still control pawn. How you provide input is completely detached from that fact.

Health is presented in the menu, and my pawn itself really doesn’t need to know health. As I said, its just a sprite. While I could make the Pawn hold its Monster state info, I decided it would just be easier to be in array on the Player Controller, as it makes ‘party’ actions a lot easier, otherwise, the player controller would have to keep up with a bunch of pawns- despite the fact only one will be active at any time.

I think my code still basically follows your first two lines: PlayerController is the brain, and my pawn is the body. The pawn basically just visually represents whatever the current fight character the player controller tells it to when possessed, but contains no logic on how damage ect is calculated. And even while that logic is called in the PlayerController, I still have it encapsulated in services in case I need to use the logic again other places.

Except if it were easier, why are we having this discussion when you could avoid the problem by having health in a pawn common to player and AI controllers? :wink:

Health is really intrinsic to the “body”/pawn, not the “brain”/controller. Even if that pawn is “just a sprite”, it’s still the in-game representation the character. Think about it, the AI brains need to know health to make decisions based on the value of their own health and that of the characters around them. On the other hand, the player controller also needs to know health, but for the completely different reason of displaying it on a HUD. Controllers share no logic but they access the same state – i.e.: a pawn.

Anyway, class design is generally a subjective choice and remains entirely up to you, but it seems to me like you’re fighting with your design rather than having it work for you. That’s generally not a good sign.

2 Likes

Thanks, last night I had moved everything into a component and that worked. At this point I could move it just as easily to a pawn if I wanted to. My game has a party of characters, but only one fighting at a time. The pawn represents whatever character that is, and at the time I didn’t want to create a bunch of pawns for the entire party as the Player Controller would only be able to reference one at a time, and the other ones are not even visible.

1 Like