Decoupling with a path-finding algorithm

Alright so I have created my pathfinding class for a TD game I am working. So far it calculates the grid using Dijkstra algorithm. and has functions that take a grid position and does the calculations as if a wall were placed or removed all internally

eg. bool AddWall(Vector3 position) returns true if the wall was placed and false if it wasn’t (nodes have a occupied bool to see if placing a wall would enclose an occupied node)

So from here I am unsure exactly how to handle linking this code to interact with creeps. Should I have functions that take a position and just return the parent and sets occupied on it’s own with many different variations of these setters and getters? Or should I allow creeps direct access to node information?

The creeps need to know where to go (current nodes parent) and nodes need to know if they are occupied. So both kind of need information from each other. But I want this information to be as loosely coupled as possible.

So My question is what would be a good way to design this code to allow them to communicate without giving too much information? Currently the creep script is empty because I can not for the life of my decide on how this should be designed.

I know design can usually vary from person to person, so keep in mind I am looking for a loosely coupled design (unless there are strong reasons to avoid such)

Thanks in advance!

In short, to answer your question about where to connect the data between Nodes and Creeps…With Decoupling set aside your suggestions are completely fine and its mostly up to preference.

I’m actually building my own TD game where the path-finding is dynamically updated as the player shapes the paths on their map. Due to the nature of my game I use a multidimentional linked-list to construct a graph and then employ an altered version of the biconnected component algorithm along with Djikstra’s Algorithm for path-finding. and due to the complexity of some of the creeps, defenses, spells and debuffs, decoupling was very important in my game.

If you’re worried about properly decoupling, then a couple of Design principals to start with:

Each class should have one and only one responsibility (or as few as possible)

This principal is a big game changer and it can explode the number of classes your project can have. The improved flexibility it will grant you will likely cost you in terms of complexity and debugging, just be aware of the trade-offs.
In this specific case you’ll likely want 3 classes:

  1. Creeps: manages actual movment and other behavior.
  2. Nodes: Tracks who it’s current neighbors are.
  3. Agent: connects Creeps and Nodes. controlling how Creeps path through the map and informing nodes then they are occupied.

In this design example, Node will have a reference to an agent (or null) to determine if its occupied, though you could have easily had the Agent store a Dictionary of nodes to creeps instead. Having the Creeps connect to an Agent class allows to find the preferred path to a destination via the neighbors its node is linked to.

program to an interface, not an implementation

Interfaces are a great way for decoupling your classes while at the same time enabling extensibility. For example, you have a map with roads and rivers. You might have one type of enemy that only walks on the road, while another that can only swim, and finally a third that can fly. All three movement modes have a common behavior, they find the next waypoint (even if how they get their next waypoint is found differently). you can simply create an interface “IAgent” that has a method Next() which your adapter classes (WalkableAgent,SwimableAgent,FlyableAgent) all implement. Your creeps can polymorphically call their agent.next() to find their next way-point without even knowing what type of class they are referencing. this has a benefit of allowing you to near effortlessly swap out agent types (maybe you have an air tower that forces a flyer to walk) and the creep is none-the-wiser.

With interfaces the implementing classes are abstracted away from the calling class so that the caller only knows what it needs to know. the more you have a class reference an interface over a concrete class the more decoupled you can make your project.

but like I said before, decoupling will make your project’s complexity skyrocket. if you have multiple programmers (even if you don’t you might in the future) then documentation is key because following decoupled code when it breaks is a headache. So its best to find compromises where you want flexibility + extensibility over simplicity + development time. Don’t forget your scope!