Skip to content

Latest commit

 

History

History
168 lines (142 loc) · 12.9 KB

Psyshock vs Unity Physics.md

File metadata and controls

168 lines (142 loc) · 12.9 KB

Psyshock vs Unity Physics

Psyshock and Unity Physics is a contentious topic with a long history. For almost every design decision Unity Physics makes, Psyshock ends up making the opposite. This table breaks down the differences. And below that is the rant that led to this divergence in the first place.

Unity Physics Psyshock
Query API Member methods Static methods
Collider Types Terrain More new types planned
Collider Representation Blob Asset Mutable struct which for some types may contain a Blob Asset
Convex Collider Beveled for performance Hard-Edged for performance
Raycasts Include inside hits Ignore inside hits (use point queries instead)
Raycast Optimizations Ignore convex bevels No bevels to ignore
Collider Casts Conservative Advancement with tolerance and max of 10 iterations Minkowski raycasts and MPR with precise results
Layers Based on masks, max 32 Based on EntityQueries or user-provided, unlimited and independent
Broadphase BVH with static and dynamic trees Multibox SAP with layer-self tests and layer vs layer tests
Collsion Events All hits stored in buffer iterated by ICollisionEventsJob single-threaded User-invoked broadphase dispatches hits immediately to IFindPairsProcessor in parallel with pair write safety
Pair Data No guarantees, requires filtering Strong guarantees based on the layers passed in
Collision World Owned by singleton Fully managed by user
Children and hierarchies Static only Fully supported (especially with QVVS Transforms)
Systems Out-of-the-box systems No out-of-the-box systems, no automatic performance cost
Simulator Modification Intermediate systems and jobs User drives each step from anywhere
Collider Scaling Uniform Scale Non-uniform stretch
Integrator Built-in damping and gravity Utilities to aid user in writing their own
Forces Explosion forces out-of-the-box Utilities for advanced drag and buoyancy for ballistics
Contact Manifolds Reports first 32 contacts found per pair Reports area-maximized 32 contacts found per pair
Collision Solver Self-contained loop Loop controlled by user using Physics.ForEachPair
Joints Strictly-defined User can fully customize inputs and even define custom constraints
Motors Several Planned

The Rant

Disclaimer: I do not think lowly of the Unity Physics developers. They are incredibly intelligent individuals who have designed some amazingly innovative algorithms that I really like and have brought over into Psyshock! My concerns here all pertain to “software architecture” and “API design”, which are typically areas mathematical and research-inclined individuals tend to struggle with.

I get asked this a lot. Why? Is Unity.Physics not good enough? Is Havok’s solution not good enough?

Trust me, when you hear my use case, you may find yourself wondering, “How the heck is Unity.Physics supposed to be a good general-purpose design?” Spoiler alert: It’s not. It’s very targeted at the use cases Unity is targeting.

First, let me introduce you to the decisions that make no sense to me:

  • Collider shapes exist as immutable shared data structures, including all masks and material properties
  • A simulation requires all steps to be ticked rather than allowing each step to be ticked manually by the user
  • Event processing jobs are single-threaded
  • Physics is very decoupled from ECS, trashing the transform hierarchy and using layers rather than the superior ECS query system; yet simultaneously, it is tightly coupled to ECS as colliders use blobs, RigidBody has an Entity field, and all of its authoring uses baking

Now, to understand why this is terrible for me, let me present to you a game idea a friend of mine and I came up with during a game jam:

The game was an RTS mixed with a TPS. You played as a witch or wizard defending a magical forest from a military force that was seeking to harvest the forest’s energy. You and your tower defense animals shot zany spells and spastic projectiles at the enemy forces. You as the player needed to manage resources and protect your land while simultaneously moving on foot to capture the enemy military bases. Oh, and the spells could have all sorts of weird effects including scaling things.

So let’s examine how Unity.Physics stacks up:

  • First and foremost, we need a Physics solution. The TPS mechanics of shooting and interacting with the world require the precision of real physics and not some weak position checks.
  • We need a mostly static environment for our characters and projectiles to traverse. Unity.Physics handles that perfectly well.
  • We need colliders on animated characters. Actually, we are still good here too. Even though Unity.Physics trashes the hierarchy, it doesn’t trash the LinkedEntityGroup, which means we can still instantiate a prefab with all of the individual colliders and reference them to drive them with bones.
  • We need morphing collider shapes. We only needed uniform scaling in this case, so Unity Physics 1.0 is sufficient. However, if we needed non-uniform scaling to sync with squishy animations, then we would have to allocate and deallocate blob assets every frame which is pretty awful, even with the convenient APIs Unity Physics provides for unique colliders.
  • Most of our game logic is going to be detecting if two colliders intersected. We want to know if our friendly projectiles hit the enemies, if the enemy fire hits us, if any projectile hits terrain, if two different spells hit each other, ect. Different collisions require different logical responses. At first, Unity.Physics looks like it should handle this fine, but this is actually where it falls flat on its face the hardest. Allow me to explain…

The first issue is we need to tell Unity what collides with what. Right now we can characterize that based on EntityQueries, because we are using Tags and unique component types for all of our other logic. But for Unity.Physics we need to encode all of that into a layer mask system. While annoying, it is doable (assuming you have enough layers).

After having told Unity what collides with what, it generates a NativeStream to be processed in an ITriggerEventsJob giving us all of our collisions. That seems nice, except all of our resulting pairs are mixed together like a jar of jelly beans. Consequently each unique event handler needs to filter through the results and pick out the ones it cares for. That’s a lot of iterating through the events. We can use EntityQueryMask to speed this up, but still, it isn’t great. Instead, we might decide to bring our iteration count back down by having one job do all the filtering for all the different handlers to react to by creating a bunch of smaller collections of pairs. This works, but now the global filterer needs to know about every kind of pair interaction. You can try to generalize this, but it is just clunky. And also, this mega-filter algorithm has to run single-threaded. So this is a pretty bad bottleneck and simultaneously is tangling all our code together in stupid ways.

What, you thought that was bad? It only gets worse.

Let’s suppose that we decide to drive our moving objects using physics, but we run into issues with the contacts and solver behavior between the military vehicles and the character controllers. We want to implement our own solver for just these interactions. Well guess what? Remember that song and dance we had to play to sort all of the collision events to the proper handlers from a single thread? We have to do the same thing again for the contacts, the jacobians, and pretty much any stage of the simulation. This time we only care about a small fraction of these events, but we still have to sift through all of them. That’s a pretty lame performance tax. All of the Unity.Physics event jobs suffer from this.

Unity.Physics feels like a simulator, not a real physics engine for gameplay. Here’s some common gameplay examples that humiliate Unity.Physics as a general-purpose physics engine:

What if I wanted to drive the colliders to spin in a circle around a character but still be individually destroy-able (think boss shields or Mario Kart’s triple shells in the newer games)? I have to set up everything with joints rather than rely on the Transform System with a rotating parent.

What if I wanted to destroy an otherwise static rock from an explosive? Well now the entire static world needs to be rebuilt.

What if I am trying to get sparks to bounce off a flash mob of vocaloids which need a dynamically updated mesh collider every frame? That cooker is expensively slow!

What if I am building a platformer and want to mimic the expanding and contracting mushrooms from the New Super Mario Bros games? Do I create a collider for every frame for those too? Or do I prebake the colliders for every step in the cycle?

What if I am procedurally generating a world and want to apply scale variations to the instantiated prefabs? More slow cooking.

What if I want to make an active ragdoll with rigid bodies for bones on an animated character? Unity Physics will break apart the entire hierarchy which will probably break animations.

What if I’m making a simple game and I just want simple triggers? Unity Physics will compute contacts for all the trigger collisions because it relies on speculative contacts for continuous collision detection (which is false-positive prone for trigger use cases anyways).

More often than not, I find myself fighting with Unity.Physics (and Havok.Physics) thinking backwards about my problems trying to not pay the cost for things I don’t need.

So I presented my concerns on the forums, and then decided that if I wanted it done right, I was going to have to do it myself. There’s always more to do, but I am comfortable and confident in what I have and the direction I am headed.