Hello,
I have circular dependencies in my project where Code in the „player“ namespace depends on code in the „combat“ namespace and vice versa. What is a good way to solve this cycle? Maybe by putting the combat code in the player namespace?
Thanks.
Decoupling
Typically via interfaces.
Sometimes that’s overkill for games.
In your case: determine what should work without the other? Can there be combat without a player? Can there be a player without combat?
Both seem to be true statements so perhaps the solution is to abstract the concepts of player-based combat. Combat system may only need to work with “combatants” which implement ICombatant interface to, for example, execute attacks and take damage. Player implements that interface, and maybe enemies too.
Does the Player need to be aware of the combat system? How it works and what it does? Unlikely. It probably suffices to give the player a way to attack, block and to take damage. That’s dealt by implementing ICombatant.
2 Likes
This will get extra fun once you use assembly definitions.
You could put them on the same namespace, or if it is player combat code you could make it namespace Player.Combat.
The best way is to get all data in via 1 direction. If the player needs to control the combat, only call combat code from the player or vice versa. It is good to make small classes that are needed for this, like a Health class. This way you do not have to stick everything in the player script.
I also suggest to look into C# Actions (or Unity Events), since these can bridge dependencies by listening to, or invoking an event from another class and call code that way.
Edit: Completely forgot about interfaces, whoops
1 Like
I think it should be mentioned that circular dependencies between namespaces aren’t really any issue at all. However circular dependencies between assembllies are not possible as assemblies essentially represent a directed graph / tree. Namespaces are just an organisational tool for type names.
Like it was mentioend, you should avoid such dependencies in the first place. If it’s necessary for some reason, interfaces are usually the way to go. Though events and callbacks could be a solution as well. Depends on the actual design and requirements.
2 Likes