I wanted to split some of the core components of my project into different gameplay modules. I managed to do that following the official docs about Gameplay Modules and these videos here.
I added dependencies in main module’s .Build.Cs file and everything seems to be working fine, I can see and use additional modules’ classes in main module’s source files.
It looks like though that I can’t do the other way around: if I try to add headers from the main module into any of the additional modules’ source files, it seems they couldn’t be found. So I tried adding dependencies for the main module into the .Build.Cs files of the additional modules, but then it spits out a Cyclical Dependency error (of course…).
So I was wondering what’s the correct way of handling this kind of situation?
Is it an option to move those parts from the main module to another second additional module that you can then use from both the main module and other additional modules (as long as that’s not going to cause another cyclic dependency)?
Additional Module (A): has some classes
Main Module (M): consists of some subset (m1) and (m2); uses Additional Module (A)
Setup after proposed change (assuming m1 is the subset of M you want to use in an Additional Module):
Additional Module (A): has some classes (just like before, unchanged)
Additional Module (B): consists of (m1)
Additional Module (C): has other classes; uses Additional Module (B) //NOTE: Additional Module (C) is optional, it's just here to show that other Additional Modules should be able to use the Additional Module B as well (not just the Main Module)
Main Module (M): only contains the subset (m2) now; uses Additional Module (A); uses Additional Module (B); uses Additional Module (C)
Yeah, that could be an option, but it involves quite a bit of refactoring and re-design. Not necessarily a bad thing, if it helps cleaning a bit.
In my particular scenario, I have the main module with all the usual gameplay classes (custom Pawn, MovementComponent, AnimInstance, etc.), while the other modules are RPG System and Level Generator. I need to be able to reference both of these additional modules’ classes into, let’s say, my Pawn class. In the same way, I need to reference the Pawn class into both additional modules.
So in your proposed setup, just for example, my Pawn class should be in yet another module?
No that’s actually not going to help you. It ONLY works in my example above because the Additional Module (B) does NOT use Additional Module (A). Applied to your case you’d still end up in a scenario where A uses B and B uses A which is yet another cyclic dependency.
The way you describe your situation you’ll have to break the “underlying” cycle first before you can separate the modules.
For example why does the Level Generator depend on your specific Pawn class? Could it not depend on just a regular APawn instead of your specific class? Or could you break the cycle using Delegates/Events: instead of calling functions directly on the pawn you could have the pawn register itself with the Level Generator and have the Level Generator notify/broadcast to all register listeners?
While this can probably work for the Level Generator, I can see that an RPG System would indeed need to be able to use some things from the specific Pawn. But what about having a special (Actor)Component in the RPG System that provides everything you need to use. Your Pawn class would only need to CreateDefaultSubobject<URPGSystemComponent>(TEXT(“RPGSystemComponent”)); in its constructor. The RPG System Module can then simply access and use this component (which it knows about since it’s part of the module) via AActor::GetComponentsByClass.
The Level Generator has a map/mini-map subsystem that I use to indicate where the player is in the game level at any time, or where other relevant gameplay items/objects are, but there are different/more suitable ways to get this kind of information (like you said, simply using APawn or even AActor class for the other elements).
About the RPG System, I think I kind of get what you mean and it could just work, but I need to do some tests to see if I fall again in some kind of cyclic loop somewhere.