How to transfer data between objects regardless of array order

I have a line of pipes containing liquid. The idea is to pump one at a time, per game tick. I have a manager that iterates through an array of all the pipes and executes the pump method in each. The problem is, if the pipes are in a random order in the array, the liquid will move through more than one pipe per game tick.


If the pipes are arranged in reverse order in the array it works perfectly, but the pipes are placeable and could end up in random order.

And

In theory there would be multiple lines of pipes, so terminating the iteration after each successful pump wouldn’t work.


So what would be the best way to ensure the liquid doesn’t skip any pipes per game tick? The second picture is what I DON’T want to happen, and the markup is how I’d like it to work.


Should I have multiple arrays for each line of pipes, and terminate after each step? Or arrange the arrays such that they are always in reverse order? Is there a third way that I haven’t thought of?
Thank you.

You may wish to consider using a different data structure than an array. Selecting a Collection Class | Microsoft Learn


A linked list , for example, might be preferable as it is always ordered properly, and insertion/removal of elements is fast. (a linked-list, is a list where each node has 2 references to other nodes (previous and next) LinkedList<T> Class (System.Collections.Generic) | Microsoft Learn


If you plan on having pipes with junctions: Since you are using a grid, you could use a modified version of a linked-list, where each node has, at most 4 possible “connections” (e.g. north south east west). [This is technically a “net” which makes simple iteration (e.g foreach) not quite a simple, as a path choice may need to be made at each node with more than 2 connections.]

Easy option:
Inside your pipe class, hold a variable,
bool received;

In your pump method, if the receiving pump accepts liquid set the received bool to true.
When it’s that pipe’s turn to pump liquid elsewhere, return early if received is true.

At the start of the tick, clear all the received values to false.

Hard option:
Instead of storing your pipes as a single array, store it as a graph.

Then each tick, traverse the graph.
The simplest form of this structure is a linked list if you need something to Google to get started.

Basically each pipe contains a reference to connected pipesit can flow to. When you add a new pipe, do a sweep of adjacent pipes to find what it can connect to.

When you remove a pipe, tell all its connection s to remove it from their own connection list.

Ok, you need to find the distance from a source and do a loop (for, foreach, while) that would look something like this:

    void GetClosestPump()
    {
        float distanceToSource = Mathf.Infinity;
        GameObject closestPump = null;
        foreach (GameObject pump in pumps)
        {
            float distance = (pump.transform.position - transform.position).sqrMagnitude;
            if(distance < distanceToSource)
            {
                distanceToSource = distance;
                closestPump = pump;
            }
        }
    }

This is what I use to find the closest enemy to my player, attach this script to a water source and it should find the closest valve towards it; for you it will require additional checks (maybe like a bool to see if the pump is at a certain value, then it skips that pump so it doesn’t drain from the water source and continue to the next pump, that way you can have the network of water in multiple sources).


For this to work, you need to create a list of pumps; I defined them as a GameObject, but it would be smarter to attach a unique script to the pumps that way you can just load the list at Start with something like this:

Start()
{
    pumps = FindObjectsOfType<WaterPump>();
}

Let me know how that works for you.