Devlog

#1 UniRx: Observe all objects in a reactive collection

Already early on in 'The Tribe Must Survive', we started using UniRx, an implementation of ReactiveX for Unity. Reactive programming helps us in tremendous ways when it comes to updating the UI whenever the model changes. Also unregistering listeners when the UI is destroyed is much easier with the reactive model compared to using classic C# events.

When using UniRx, you often face the following two scenarios:

  1. Observing a single value:
    E.g. an antelope moves and therefore changes its position. You would like to observe its position so that you can change the position of the corresponding MonoBehaviour.
  2. Observing a collection of objects:
    E.g. a herd contains many antelopes. You would like to observe the entire collection, so that you are notified, when e.g. one antelope was removed from the collection.

These two cases are rather simple and with UniRx, there are e.g. ReactiveProperty and ReactiveCollection to support them.

But what if you want to combine these two cases?
How would you observe a reactive property on all the objects in a reactive collection?
How would you e.g. make the center of a herd observable?

To solve this problem, we came up with a solution, which allowed us to both. We can observe any changes to any antelope's position in a reactive collection and we also observe changes to the collection itself:


public IObservable<Position> CalculateHerdCenter() {
    var herd = new ReactiveCollection<Antelope> {new Antelope(), new Antelope()};

    // The parameter is a Func which maps an antelope to a IObservable<Position>
    return herd.Observe(antelope => antelope.OnMovedAsObservable())
        .Select(_ =>
        {
            // This calculates the center of the herd
            var r = herd.Average(a => a.CurrentPosition.R);
            var q = herd.Average(a => a.CurrentPosition.Q);
            return Position.Of((int) r, (int) q);
        });
}

// In the extension class
public static IObservable<TValue> Observe<T, TValue>(this IReadOnlyReactiveCollection<T> collection,
    Func<T, IObservable<TValue>> observableFunc) where T : class
{
    return new CollectionObservable<T, TValue>(collection, observableFunc);
}

The actual implementation can be found on GitHub.