we've already shown the space platform processing cycle in FFF-380. Let's talk a little bit more about the machine which makes it all possible - the asteroid collector.
When I was working on the early space platform concepts trying to figure out how movable ships would look, we knew that we wanted some way of getting parts of asteroids onto the ship but hadn't decided how that would be done. At this early stage all of the new structures had very simple placeholder graphics.
Although we start with grey boxes, it's best to get those replaced with something that looks more appropriate as soon as possible. The earliest placeholders are usually a quick sketch of something in the right general shape, something kitbashed (patched together from existing graphics), or something grabbed from my mods. For the asteroid collector, a fairly generic side-mounted structure was used as the placeholder. If we stuck with something like this then presumably it would use some sort of tractor beam to pull in asteroid chunks. Tractor beams (or electro-grapples, etc) are the easy fallback option for this sort of situation because it's a simple concept to animate and code.
A tractor beam just didn't feel very Factorio. It's too far into the magic-ethereal-sci-fi direction and it's used all the time. I wanted something more mechanical and Albert agreed, tractor beams were out. It's not that Factorio doesn't have high tech stuff, it's just better to do it in a way that's more tangible and industrial when we can.
I invented a lot of alternative options but here are 5, along with some proof-of-concept sketches.
1: Rocket-guided harpoon or bag
Small rockets that would wrap the asteroid chunk in a bag on impact, then pull it to the collector mouth using a tow line.
2. A loooong inserter-like arm
Like an inserter but dedicated to grabbing asteroid chunks. It would put the chunk into the machine “mouth” or directly on to a belt behind.
3. A paddle
Like the inserter arm except that it doesn't need to grab the chunk, it just needs to bat it into the direction of the collector mouth.
The collector itself would just be a hole in the platform side that only collects chunks that happen to fall into it. This would be paired with a set of wedge-shaped blocks you could add to the front of platforms so that if you are moving and a chunk hits the wedge it glances off, and this way you could guide chunks to the collector hole. This idea sounds fun initially because it makes the front shape of the ship important, but the downside is that most ships would just end up as V, W, or WWW shapes.
5. A flexible snake arm
Unlike the inserter-style arm this would act on the same ground plane as the asteroid chunks and the platform itself, meaning it would need to snake around any protruding defences. This is an interesting option to build with as the platform shape affects interception time.
I thought I could do something pretty cool with the flexible arm idea. I started with a set of sketches trying different machine structures and collector mouth shapes. The arm itself is most similar to the design of real-world snake robots, but with it being anchored at one end, it quickly became known as “the tentacle”. It is also worth noting that I moved the crushing function out of the asteroid collector to a dedicated machine. This lets the crusher be used again by some late game advanced recipes and expand the middle of the platform.
Below is the concept art I presented to Kovarex. Up to this point people were skeptical about the snake or tentacle idea so it was down to the concept art to win people over otherwise we might be back to square one. Fortunately as soon as Kovarex saw this he was on board and other people were convinced too. Hrusa stepped in to make it a reality, so he can take it from here.
Taking a step back in time now, let's talk about how the gameplay side of things unfolded in the meantime.
As a keystone feature of the platform production cycle, the collector started its life under Earendel's hands as a gray box. It was a simpler time. Asteroids were not even a thing back then! So the machine was just an assembler which continuously crafted chunks out of thin air.
Once we had asteroids in the game, the first version that actually collected them cast a simple rectangle around it. Any entering chunks would immediately get teleported into the collector's inventory. Nothing flashy, but enough for playtesting.
The next advancement was to collect asteroids mechanically. Rseding kicked things off by implementing Earendel's earlier harpoon idea:
At this point the collector could be placed anywhere on the platform. Requiring it to touch the edge (similar to an offshore pump) was considered, but no conclusions had been made yet. Testing with self-imposed limitations took place and quickly exposed a crucial problem.
Since the effective reach was relatively long, the optimal strategy was to always inset the collectors away from the lucrative platform edge. Such setup invited little decision-making from the player. The entire edge could be dedicated to turrets while the interior provided plenty of room to unload and process chunks. On top of all that, the visuals of harpoons or arms reaching over other buildings for chunks at platform-level were dubious at best.
The logical conclusion was that the collectors should require access to open space. This happens to create an interesting dynamic between defenses and resource collection and rewards efficient belt usage. That was at the time when Earendel was putting together the concept for a more organic-feeling bendable arm, which ultimately made it into the game.
Now it was up to me to figure out how to make it all happen. With the expected behavior laid out, I could separate the task into a multiple individual features—quite like you do while playing Factorio itself. You want to move at increments which can work on their own.
For reasons that will be helpful later I've decided to express a particular pose as a segmented line—a series of angles starting from the base. Additionally, each pose includes an extension value. This way, the arm can slide up and down along the length of the curve without calculating any new points.
Next I tried adding a control curve which put some swing into the arm movement, but it turned out a little...
While thinking about the collector, we've decided it needs to be able to harvest additional chunks after it has been extended. Since the arm is represented as a series of angles, we can blend between the starting and desired pose by simply blending each individual one.
Ideally we would want the claw to move at a consistent speed. However, because we are blending angles, said speed varies greatly. Depending on whether the arm is extended to 10 or 20 units, turning the base by 5° will lead to vastly different results. Even though the arc of the traveling collector appears circular, it is in fact a complex shape which cannot be easily measured.
So instead, I found a way to weight all the individual angles together quickly and project the arm position onto a circle. Then I measure the distance between two arm poses on a circle to get a good consistent estimate of the traveling distance.
Simulation of the arm blend I've made to test my calculations. The arms are projected onto the teal circle to estimate the arc travel distance.
The final design problem is that there is a lot of different ways for the arm to travel between two poses. That is because of the angular speed relationship mentioned above. The shorter the arm is, the faster it can turn. But it also takes some time to retract and extend to reach the optimal traveling distance.
Just like with the arc distance, I've spent some time trying to express the mathematical problem and do a true solution. Then I realized that I can get really close with much less effort. Instead of searching for the absolute best extension to swing at, I choose extensions at some fixed interval, compute the distance traveled for each and select the best one. In practice you can't even tell whether the path is ±0.5 units from the absolute best arc there is to take.
Showing three of the paths considered for movement. Increments at which the turning radius is considered are highlighted.
Around this time, we started debating the performance cost. Kovarex liked the feel of the arms a lot, but wanted to ensure that all the links being simulated would not impact large factories with dozens of platforms. To that effect, we could use the previous step. Since we've ensured that the claw travels at a predictable speed, we do not need to update its position every tick. Once the desired path is found, we store its distance. Then we increment a single internal number which tracks how far along this path the claw has traveled. Only if the arm is visible to the player, do we evaluate the actual intermediate arm pose.
With the arm motion covered I moved over to the pathfinding problem. The initial idea was to pull a classic A* and map out all adjacent tiles to find the shortest path to the collector from each one. However, the records of each tile took up a lot of space and the resulting paths ended up being very jagged which looked terrible and required further processing. Furthermore, the collectors often sit around wide open areas. Traversing those tile-by-tile every time a chunk floats by is an absolute overkill.
In the light of this observation, I've decided to implement a navigation mesh. A navmesh covers the open areas with large rectangles. Then it casts rays to find all direct connections between significant corners of those rectangles. The construction and maintenance of the navmesh takes some computational power whenever the player builds platform tiles, but in return the much more frequent pathfinding is greatly sped up. Even better, the paths we find on a navmesh are the shortest and simplest possible.
To make the polygonal navmesh path more palatable, I approximate it using a Non-uniform rational B-spline (NURBS), which smooths out the sharp corners. The nice property of NURBS is that it quite reliably hugs the guiding points. That makes it very popular in 3D modeling applications.
Besides smoothing, there is a few other minor tricks I use to enhance the look:
One known issue around the time I was starting out with the collector was platform speed. With high quality thrusters, the platform could eventually move so quickly that turrets could not turn fast enough to shoot incoming asteroids in time. I knew this could be a big problem for the collector as well.
The collectors cannot just constantly test every chunk every frame. It was necessary to narrow down the selection. To that effect, every time a new chunk spawns, it will cast a line forward and register with all collectors along the way.
Since the limiting factor is availability of arms, it is the arm which checks for incoming chunks whenever it finishes its previous task. This design gives us room to project the chunk position forward based on how long it will take the given arm to get to it and arrange a punctual rendezvous. Additionally, super fast chunks no longer pose a problem, because even those will be registered in advance. The arms move into position and intercepts the chunks the moment they touch the collection area.
Predicted chunk movement with connections to registered collectors (sped up).
Once all the major work was wrapped up, it was just a matter of finishing off the smaller details before the collector was ready to go in to the master branch. This is things like the building restriction, blueprint support, copy paste, etc. Since the Navmesh and NURBS system were all new code, there was a lot of writing tests, chasing down edge cases, and fine-tuning the performance.
Overall in our playtesting we're happy to say that the Asteroid collector works really well to give the platforms the unique look and gameplay that we were hoping for.
As always, we look forward to collecting your opinion chunks at the usual places.