You got me there! I initally thought that you'd be able to circumvent that by splitting collision into two cases, but I now realise that it's not really worth the code complexity (and hence risk of unintended behavior) just to fix this one minor issue.
I spent the last couple hours trying to find a better solution, but rigid body collision is really complex. Thanks to angular velocity, even two body collisions need a substantial amount of math to do correctly, as seen in e.g.
this article from
this series. But the main problem is that there're plenty of possibilities in factorio, where multiple entities collide simultaneously with each other, and the best thing I found in that regard was a full blown
matrix solver.
And even I, who has no fear of any math, would hate to implement such a thing, especially since there's not much to gain. Even if you'd be a god like programmer (and you at Wube sure seem like it), there's no guarantee that such a solution will even work as intended and solve all issues, even though the simulation would be perfectly realistic
But before giving up entirely, I managed to remember that there's another problem in factorio connected to this one. And I already invested quite some time thinking about that one, but ultimately decided to not bother you about it because I'm quite possibly the only one doing it right now: cars on belts.
I was particularly bothered by the fact that my fully set up car based smelter producing iron, copper, steel and engines for 10k spm took and still takes a whopping 11.5ms update time, which will be about half out the expected total.
Allow me to describe a potential "fix" for both problems, trains and cars:
First, we'll divide all entities into three categories for collision detection:
- First, there're all non moving buildings like assemblers, power poles, wall etc. These buildings don't have active collision, but they're rather only there for other entities to collide against
- Second, there're all actively moving entities. But that I mean all entities that not only move, but also decide their velocity and direction on their own. Examples for this are mainly biters and players, as well as players driving cars and tanks
- Third, only passively moving entities remain. These are entities that are either moved by something else, or you can treat them as such. Trains and cars/ tanks on belts fall into this category
Let me explain why such a seperation is useful:
Seperating buildings is kind of obvious, since they then don't need to be updated, and thats the best kind of update!
Collision detection for the second group falls into two cases: collisions for biters can make as many sacrifices as necessary for the sake of fast computation, since there's simply no need for high precision collision, while collision for players is allowed to make huge sacrifices in performance to ensure accuracy/ good gameplay, since even 100 players are a drop in the buckets compared to tens of thousands of active entities. The most important thing about this group is that collision doesn't need to be easily predicatable (exactly). Biters die under a barrage of gunfire or in a radioactive cloud regardless of their exact position, while players are impossible to control so precisely anyway.
I'd say that the current collision detection is perfectly fine for this group, as its decently fast, modders don't seem to have problems with it (as far as I know), and the gameplay feel is also optimal.
The reason that trains and cars on belts should get their own collision is that optimization fundamentally builts on information: the more information there is about an object, the more chances you'll get to find a creative way of using that information to do fewer calculations. And trains as well as cars on belts have a huge piece of information accompanying them: they'll only move along a predefined and rarely changing path!
And I think its possible to exploit that knowledge to optimize and improve collision detection, so let me explain my thought around that topic:
Comming first is of course the interplay with the other two types: collision with passive obstacles can be optimized by telling each rail piece/ belt piece its sideways clearance as a list of nearest collidable buildings in each direction, indexed by their distance. This not only allows cars to simply check the belt they're on to decide whether or not they get hung up on a building to the side, but trains also easily detect other rails merging into them!
Entities in the second category can be rather easily ignored, since it's their job to collide against a train/ car. You may raise the question how collision response should look like in this case, and my answer is that entities in the third category should get movement priority over those in the third!
The reason for this is mainly quality of life: trains and cars on belts were planned to be there, and hindering their movement could wreck finely tuned setups. I can't count how often I ran into cars on belts and killed its delicate timing
Furthermore: trains kill everything in their path instantly anyway if they're going fast, and I argue that it's acceptable that they push everything infront of them away at low speeds. A thousand kilo car (which would be a rather small one) on an industrial conveyor also shouldn't be stopped by a mere hundered kilo human in it's way: the enginieer should be pushed around.
All in all I suggest that second category entities should watch out for third category entities, and always make space for them. Since third category is routed anyway, it's rather simple to find a direction in which one would leave the track.
Having the third category entities be bound to their tracks makes it rather easy to find them, too: Second category entities check for buildings around them anyway, so rather than ignoring belts and train tracks, they instead use the pointer to them to find the corresponding transport line and from there any third category entity can be found.
Lastly, the collision between third category entities themselves can be handled via my previous method if they're on the same line, and collision between lines can be done in a similar way by following the clearance pointer to neighboring lines. As mentioned in my previous post, collisions here would be able to correctly take the entity speeds into account.
In summary, I think that this collision system improves the current one by abolishing update order dependencies on automateable entities, makes delicate systems more robust by giving them priority, and last but not least has a pretty good shot at being better for performance!
PS: This system would even allow sideways facing inserters like seen
here to bind to the belt beside and sleep until container like cars come into range, because it could check for them and their clearance.