Physics settings and LOD
PhysicsSettingsAuthoring defines global simulation limits and LOD (level of detail) tiers. Bootstrap packs values into a persistent blob on PhysicsFixedSettingsComponent and copies runtime-tunable fields to PhysicsVariableSettingsComponent.
LOD lets the simulation spend more collision budget on nearby bodies and relax work for distant ones. That trade-off enables large crowds and particle fields, but loose limits reduce determinism. Tighter tiers and closer camera ranges improve consistency.
PhysicsSettingsAuthoring
Add PhysicsSettingsAuthoring once per baked world. Bootstrap waits for this component before allocating native structures.
Global fields
| Field | Type | Default | Description |
|---|---|---|---|
MaxEntitiesCount | int | 10000 | Maximum bodies imported each frame; excess entities are ignored |
AirFriction | float | 0.5 | Global linear air friction on dynamic bodies |
AirAngularDrag | float | 0 | Global angular air drag |
PushOutPower | float | 1 | Multiplier on penetration push-out strength |
PushOutType | PushOutType | Position | Apply push-out as position or velocity correction |
SubstepsCount | byte | 1 | Inner physics loops per time-scale iteration (1–4) |
CollisionCheckSettings | struct | both true | Toggles for dynamic–static and dynamic–dynamic pair collection |
ShouldDebug | bool | false | Creates entities for the Pairs debug window |
CollisionCheckSettings
| Field | Default | Description |
|---|---|---|
CheckDynamicVsStatic | true | Include dynamic–static pairs in broad phase |
CheckDynamicVsDynamic | true | Include dynamic–dynamic pairs in broad phase |
The Unity layer collision matrix is also baked into the settings blob at bootstrap. Use PhysicsFixedSettingsComponent.IsColliding(layer1, layer2) to query it at runtime.
Push-out (PushOutType)
After narrow-phase contacts (object-to-object and surface), the solver builds a push-out velocity from penetration depth, contact normal, PushOutPower, and the current linear velocity. PushOutType chooses how that velocity is applied to the dynamic body:
| Value | Behavior |
|---|---|
Position (default) | Integrate push-out into Position this substep (position += pushOutVelocity * DeltaTime) |
Velocity | Add only the missing speed along the push direction to Linear velocity |
Both CollisionVelocitySystem (pairwise contacts) and SurfaceCollisionSystem (surface contacts) resolve with the same switch. Object-to-object resolution:
switch (PushOutType)
{
case PushOutType.Position:
{
positionData.Position += pushOutVelocity * DeltaTime;
break;
}
case PushOutType.Velocity:
{
float pushOutLength = math.length(pushOutVelocity);
if (pushOutLength > 0f)
{
float3 pushDir = pushOutVelocity / pushOutLength;
float existingSpeed = math.dot(velocityData.Linear, pushDir);
velocityData.Linear += pushDir * math.max(0f, pushOutLength - existingSpeed);
}
break;
}
}
Surface collision uses the same Position path; for Velocity it takes the push direction from -result.Normal instead of normalizing the averaged push-out vector.
LOD tiers
Use the LOD list on PhysicsSettingsAuthoring to add up to four distance tiers. The Inspector draws each tier in the Scene view (matching colors in the property window).
| Control | Behavior |
|---|---|
| Add / Remove | Insert or delete tiers; at least one tier is always present |
| Last tier | Always the default fallback for bodies outside all nearer ranges |
Range | Distance from the camera for this tier; must increase between tiers |
VisionAngle | Vision cone half-angle (degrees) used with Range to test visibility |
Set
VisionAngleto 360° when possible — it simplifies LOD indexing to a distance sphere and avoids cone edge cases.
When more than one LOD tier exists, bootstrap creates PhysicsStepComponent. Each late-update import pass, PhysicsStepSystem writes Camera.main position and forward into that singleton, and ImportPhysicsDataSystem assigns each body a LodIndex stored in PhysicsBodyData.
LOD assignment tests the body’s bounding sphere against each tier’s spherical cone, from nearest to farthest. The first matching tier wins; if none match, the body uses the last tier index.
With only one tier in the list, camera LOD is disabled and bodies keep the authored LodIndex (default 0).
Per-tier capacity limits
Each LOD tier stores five limits as int3 vectors. The three components are the values used at time scales ×1, ×2, and ×4 (LittlePhysicsTimeComponent.TimeScale).
| Field | What it limits |
|---|---|
DynamicsInCells | Max dynamic and kinematic body indices stored in one spatial-map cell per substep |
StaticInCells | Max static body entities stored in one cell. Set to 0 if the scene has no static bodies |
CellPerEntity | Max cells a dynamic or kinematic body may occupy when its AABB spans multiple cells. Excess cells are randomly subsampled |
PairPerEntity | Max unique collision pairs collected per body during broad phase |
CollisionPerEntity | Max narrow-phase collisions stored per body — the heaviest step; keep lower on distant LOD tiers |
At runtime, PhysicsLodData.GetElement(timeScale) resolves the active PhysicsLodElement for the current substep. Internal systems call GetLodData(timeScale, lodIndex) on the settings blob when inserting into the spatial map, collecting pairs, and writing CollisionData.
Bootstrap allocates native buffers using GetMaxLodData() — the maximum value of each field across all tiers and all time-scale slots. Per-frame work still respects the active tier for each body.
Default tier values
New tiers start from PhysicsLodData.CreateDefault():
| Field | ×1 | ×2 | ×4 |
|---|---|---|---|
DynamicsInCells | 32 | 16 | 8 |
StaticInCells | 8 | 8 | 8 |
CellPerEntity | 32 | 16 | 8 |
PairPerEntity | 32 | 16 | 8 |
CollisionPerEntity | 16 | 8 | 4 |
When TimeScale is 2, the middle column applies; at 4 (and 3, which maps to the same slot), the third column applies.
What LOD affects
LOD caps object-to-object work inside the spatial map:
- Cells a body occupies (
CellPerEntity) - Neighbors seen per cell (
DynamicsInCells,StaticInCells) - Pairs and contacts per body (
PairPerEntity,CollisionPerEntity)
LOD does not reduce:
- Gravity or friction on dynamic bodies
- Surface collision — every dynamic body is tested against all surfaces every substep
- Import/export of body transforms and velocities
If lists or maps are full, TryAdd silently drops entries. Use the Pairs debug window or enable ShouldDebug when tuning limits.
Runtime access
Fixed settings live in the blob referenced by PhysicsFixedSettingsComponent.BlobRef:
ref var blob = ref SystemAPI.GetSingleton<PhysicsFixedSettingsComponent>().BlobRef.Value;
int maxBodies = blob.MaxEntitiesCount;
int lodTierCount = blob.LodData.Length;
// Capacity for body at lodIndex during time scale 2
PhysicsLodElement limits = blob.GetLodData(2, bodyLodIndex);
Variable settings (air friction, substeps) are on PhysicsVariableSettingsComponent and can be changed at runtime without rebuilding the blob.
Tuning workflow
- Start with defaults and a single LOD tier while validating basic collisions.
- Add tiers with decreasing capacity columns for distant
Rangevalues. - Enable
ShouldDrawInEditoronSpacialMapAuthoringand confirm bodies stay inside the map volume. - Raise
MaxEntitiesCountonly as needed — it sizesBodiesList, collision arrays, and related native memory. - At ×2 or ×4 time scale, expect stricter effective limits from the ×2 and ×4 columns; increase those columns if distant bodies lose contacts when speeding up simulation.
Related
- PhysicsFixedSettingsComponent — blob layout and layer matrix
- PhysicsVariableSettingsComponent — air friction, push-out, substeps
- LittlePhysicsTimeComponent — time scale selects the ×1 / ×2 / ×4 column
- PhysicsBodyData —
LodIndexfield on each body - How it works — LOD — pipeline overview