What would be the best way to implement the following player system to avoid tight coupling?
multiple input modes: can be either from PS4 controller or keyboard and mouse.
the amount of items in the player’s inventory affects the movement speed of the player.
the weight of the player affects the movement speed of the player.
the amount of items in the player’s inventory affects how happy the player is.
It’s clear to me that each input mode should implement some input interface, but what about the other stuff?
For example, I could have a InventorySystem class, and it needs to affect the MovementSystem, but isn’t it tight coupling if the InventorySystem has a reference to MovementSystem directly?
Isn’t this tight coupling:
public class InventorySystem {
public int amountOfItems;
private const int MIN_ITEMS_TO_MOVE_SLOWER = 5;
private const int HIGH_ITEM_COUNT_SPEED_MULTIPLIER = .2;
// tight coupling (right?)
public MovementSystem movementSystem;
// this would be called by the Player or smth, as this doesnt inherit from monobehaviour
void Update() {
if (amountOfItems >= MIN_ITEMS_TO_MOVE_SLOWER) {
movementSystem.speed *= HIGH_ITEM_COUNT_SPEED_MULTIPLIER;
} else {
movementSystem.speed *= 1/HIGH_ITEM_COUNT_SPEED_MULTIPLIER;
}
}
}
Should I maybe have something like:
public interface IMovementSystem {
public float movementSpeedMultiplier;
}
public class MovementSystem : IMovementSystem {
public float movementSpeedMultiplier;
// other details
// ...
}
Its tight coupling if it goes both ways and starts to depend on one anothers state. A one way dependency is not really an issue at all. If one component references another to simply call methods or pass data to it… that’s normal C#/Unity things.
You can move the responsibility into a separate component as well. I’d agree, the component that handles the players inventory should do just that. A separate component can reference both other components, and handled these changes in movement speed.
This is pretty much exactly how it works in my current project, though as its physics based, the InventoryWeightHandler simply updates the mass of the rigidbody when items are removed or added from the player’s inventory.
In any case, worrying too much about this stuff will lead to analysis paralysis and cause you to not get anything done. It’s more productive to make it work first, and then if your requirements change, simply refactor from there. The lessons you learn when refactoring will help guide your future design choices.
Yeah, in general a dependency to a concrete class is not a problem at all, and there’s no need to introduce an interface, unless you have an actual need for modularity.
However, there is also cohesion, and I do have to say that it feels a little bit weird to me that an InventorySystem would have knowledge about a MovementSystem. It does feel to me like optimally you should be able to modify your InventorySystem, without also having to go modify your MovementSystem in tandem.
You can make it so that the two systems don’t know about each other, by introducing a mediator between the two classes. Instead of InventorySystem talking to MovementSystem directly or vice versa, the mediator will take care of all communication between them.
For example, you could inject something like this to your MovementSystem as IMovementSpeedModifier: