The problem: colliding large, concave, moving 2D objects using small quads or circles as colliders to define the overall object boundaries. The small colliders are dynamic: they can be added, removed, or their shapes and positions changed. This doesn’t appear to be a typical physics scenario, so I wrote two implementations for Unity3D: one using its own 3D physics and another integrating Farseer physics.
- Built-in: It’s already designed to work with the transform system, and has gizmos for colliders.
- Decent API: Unity has plenty of API oddities and gotchas, but the physics fit in sensibly with the rest of the system.
- Black box: Not always clear what’s happening as a result of changes or calls I make. This has led to one instance where I ended up with odd “popping” effects upon adding or removing colliders when rotation was enabled on objects and I wanted to use my own moment of inertia value. I did not pursue the problem further.
- 3D: This adds a slight inconvenience in regard to constraining dynamics. It may also mean reduced performance because of greater complexity compared with 2D, although for my purposes I have yet to see problems.
- Overhead: Each collider requires its own GameObject, which worries me when it comes to performance at scale. It comes back to the black box issue; it may be performant enough, but it’s an unknown that could change.
Farseer is a C# port of Box2D. It was originally developed for XNA, but written to work as a stand-alone library as well.
- Open-source: It’s great that I can jump in and see exactly what the library is doing. This also makes it easier to test; I can run the simulation in a test environment, allowing me to build automated physics integration tests.
- 2D: Potential for better performance compared with a 3D library.
- Integration effort: This means synchronizing between Farseer bodies and Unity transforms. Gizmos needed to be implemented, but fortunately much of the work was done in Farseer’s debug view and in this port.
- API: It felt like the functionality was all over the place and a lot of internal details exposed (possibly for good reasons). It’s probably difficult to make changes at a point where it’s so widely used, and I’m sure much of it is the result of performance tuning and its history.
- Code changes needed: Dynamic fixtures did not work well out of the box: Farseer does not appear to be designed for rapid fixture/shape changes. I had to write my own methods to create/pool fixtures and update shapes which relied on internal details of the library. I also notice a number of places where fixtures are removed from array-based lists by shifting the following array elements, which could conceivably result in poor performance with a large number of objects.
For both, I’d like to have better control over when body properties are automatically recalculated. In my case, I don’t want them to ever be recalculated from the shapes composing them because I already maintain those calculations and because the shapes do not represent the entire object. I haven’t stress-tested them both for a performance comparison, but I’d have a hard time believing Farseer would fare worse.
Another option I’d like to consider is Chipmunk, a 2D C library which appears to have a fairly clean API. Unfortunately, I don’t see any C# ports maintained, and I’m not about to roll my own (and maintain it..) without a compelling reason.
At this point I’ve decided to go with Farseer. It appears to work, I’ve gotten past some of the messy details, and it allows me to test physics-dependent functionality independently from Unity. That last point is the most compelling, and probably won’t change even if Unity gets a 2D solution that otherwise fits my needs.
This comparison came with some side benefits to the codebase:
- Isolating coordinate mapping: Unity has a left-handed coordinate system, and in the past I’ve had the camera looking in the -Y direction, with the X-Z plane containing the 2D world. I took this opportunity to instead map to X-Y with the camera looking in the +Z direction, defining a common set of methods to support either.
- Decoupling physics: During the transition, I could swap back and forth between physics implementations to compare results. I’d like to maintain this ability to mock out or swap physics libraries in the future.