It is straightforward to implement a worker that simulates flocking
Suppose your game world includes birds. In the real world, birds exhibit flocking behaviour, an interesting phenomenon that clearly illustrates how individual agents following simple rules can generate a much more complex behaviour at the group level.
It is thought that a flocking bird is subject to three basic urges. The first is to stay close to its neighbours; the second is to follow the same direction as its neighbours; and the third is to avoid crashing into its neighbours.
In a traditional game architecture, it can become prohibitively expensive to implement this behaviour when thousands or millions of birds need to be simulated. With the Entity-Component-Worker architecture, however, it is straightforward to implement a worker that simulates flocking and integrate it with the simulation, without competing for resources with the rest of the workers.
In this article, we’ll show you how to build a custom SpatialOS worker that provides flocking behaviour for your game.
Each worker instance is launched by SpatialOS, and passed the IP address and port to which is needs to connect. At startup, the worker just needs to connect to the simulation. This is done here.
Instead of delegating the Transform component to a physics engine, we delegate it to the Flocking worker. This is done in this part of the code, that runs in the logic worker.
Whenever SpatialOS decides that the worker is granted authority over a given entity, we need to capture that operation, and maintain a list of birds that we should simulate. This is done here.
It is important to remember that, in general, a worker will have read-only access to a larger set of entities than the set of entities it has authority over: to implement flocking for an entity, the worker needs to know the positions of the entities surrounding it, even the ones it’s not simulating itself.
As mentioned above, a bird is subject to three basic urges. These urges are expressed through a coefficient weighted vector sum, and the resultant vector serves as a steering vector for each bird to follow. We calculate the steering vector here.
The next step, done here, is to loop over each entity the worker has authority over, and then find its closest neighbours. Neighbours are only considered if they are within SearchRange() and in front of the bird in question.
updateComponent calculates and then applies the steering vector to the position, velocity and facing vector states of each bird.
Flocking is done entirely in the flocking worker, so the only code that runs in the logic worker deals with spawning the bird entities (found here), and delegating their position and rotation to the flocking worker (found here).
We can run a demo of this flocking behaviour immediately in the development machine:
There are many advantages to creating workers in this way. C++ is a stable and well-integrated language. Thousands of off-the-shelf SDKs could be easily integrated, to help achieve anything from pathfinding to protein folding. Using the C++ API as a starting point, it is possible to create a reusable framework for new workers in any programming language that has a C++ API or C++ bindings.
The development of this simulation was done a development machine using the SpatialOS SDK; the local workflow enables quick iterations while developing and debugging the custom flocking worker.
Once the simulation is tested locally it can be deployed to the cloud, and run it at a much bigger scale without making changes to the code. Each worker is given authority only over a subset of the entities; if a worker is overloaded, SpatialOS will automatically start up a new one to spread the workload.
This means that the simulation is not limited to what a single flocking worker can do. Indeed, our local deployment used only 200 birds, but when we spawn a flock of thousands of birds in the cloud, SpatialOS automatically runs 3 instances of the flocking worker to handle the workload.