Orbital Fleet Behavior

This blog post is a bit different than usual – instead of talking about a major new game mechanic, I’d like to instead take a quick – but in-depth – look at something relatively minor, but that I thought was interesting. I will, of course, continue to write regular-style blog posts as well.

In Starsector, fleets will often orbit a planet for some time – trade fleets offloading cargo, patrols preparing for duty, and so on. The orbits of some well-to-do colonies can get quite crowded, with fleets overlapping each other and being difficult to pick out. This isn’t a huge problem in terms of game mechanics, but it’s still occasionally inconvenient and just messy to look at.

orbital_mess

The other day, encountering a particularly egregious case, I wrote a quick algorithm for the fleet AI to use to avoid overlapping other orbiting fleets  as much as possible.

orbital_not_mess

So, what’s actually involved in getting this behavior? While nothing here is terribly complex, there is a surprising number of pieces. The calculations are also performed by each fleet independently, but designed so that effectively cooperative behavior emerges when all the fleets apply the same behavioral rules.

Algorithm
The first order of business is for a fleet to get a list of neighbors that are too close for comfort. That’s easy enough – just a distance check with some padding. The fleet then figures out the average location of these neighbors, and moves forward more quickly along its orbit, but only if the average location is behind it. The effect is that fleets that are already at the front of a pack will pull forward, while other fleets will continue along their slower-paced orbits. The important part is that the actions of the fleets will not conflict with each other.

An initial version of the algorithm made fleets in the back of the pack move backwards, as well. While that produced quicker separation, it also felt too hectic – we generally want stately orbiting, not chaos.

Using just the above was almost good enough, but fell short when there were too many fleets for an orbit to hold, leading to endless jostling. Of course, at some point there could be too many fleets regardless of anything we might do, but still, it’s possible to do better. So, in  addition, each fleet looks at its nearest overlapping neighbor. If the neighbor is smaller, the fleet moves to a higher orbit; if smaller, to a lower one.

This results in 1) larger fleets generally going to a higher orbit, where there is more room, and 2) cooperative behavior without explicit communication between fleets. The smaller fleets go down, and the larger fleets go up, just due to the nature of the logic. Like cars going down the right side of the road without having to talk about it.

The next step is to minimize the recurrence of overlapping – again. looking for stately orbiting, not a scramble. To achieve this, fleets set their desired angular orbital velocity to the average of their neighbors, effectively matching speeds as the de-overlapping is completed.

Finally, these calculations are all performed at randomized intervals, instead of on every frame. The benefit is twofold – first off, performance is better (though the number of fleets is usually low enough that it could be done every frame if need be). The more important benefit is that randomizing the timing means that fleets won’t get into any repetitive movement cycles when faced with unfortunate starting conditions. For example, one fleet might continuously bounce between two other fleets – but with randomization, this outcome is extremely unlikely and won’t keep happening for long in any case.

Here’s a gif of it in action:

 

 

Comment thread here.

Tags: , , , , ,

This entry was posted on Tuesday, March 20th, 2018 at 6:39 pm and is filed under Development. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.