Inside the pipeline

Mid-step custom logic runs in LittlePhysicsUserSystemGroup — after internal collision and surface systems finish, and before PhysicsVelocitySystem integrates velocity into position data.

Where it sits

Each fixed update, LittlePhysicsSystemGroup runs the inner pipeline time scale × substeps times. Every iteration executes three child groups in order:

Order Group What completes before your code runs
1 LittlePhysicsInternalSystemGroup Gravity, friction, spatial map, object-to-object detection and velocity resolution, surface collision
2 LittlePhysicsUserSystemGroup Your systems
3 LittlePhysicsLateSystemGroup Velocity → position integration in native buffers

Your systems see current-substep collision results in CollisionsSingleton and live body state in BodiesList. Changes to velocity or pose you write here affect the integration step that follows immediately.

Read native structures, not ECS components

During the fixed-step loop, internal systems operate on unmanaged buffers attached to PhysicsStructuresComponent:

Need Read from
Body pose, velocity, shape, flags BodiesList[index]
Object-to-object contacts Collisions.CollisionDataMap
Surface contacts Collisions.SurfaceCollisionMap
Broad-phase cell membership CollisionMap.DynamicMap / StaticMap
Entity → body index EntitiesMap
Per-body RNG Randoms

ECS components such as PhysicsBodyComponent and LocalTransform are updated on import and export, not between substeps. Querying them mid-loop reads stale data.

Export copies native results back to components once per frame, after all inner loops complete.

Time and iteration count

Each inner iteration sets LittlePhysicsTimeComponent:

  • DeltaTime — fixedDeltaTime / substepsCount (not multiplied by time scale; the outer loop runs TimeScale iterations instead).
  • TimeScale — current scale value (0, 1, 2, or 4).
  • ElapsedTime — accumulated physics time.

Multiply forces and impulses by DeltaTime when integrating manually. Example: the Scene 4 — Trigger sample scales attraction power by DeltaTime before applying velocity in an ICollisionJob.

When time scale is 0, the inner loop does not run and LittlePhysicsUserSystemGroup systems are not invoked.

Scheduling

Annotate systems with:

[UpdateInGroup(typeof(LittlePhysicsUserSystemGroup))]
public partial struct MyMidStepSystem : ISystem { ... }

Prefer package job interfaces and ScheduleAndChain:

[BurstCompile]
public void OnUpdate(ref SystemState state)
{
    var structures = SystemAPI.GetSingleton<PhysicsStructuresComponent>();
    ref var simulation = ref SystemAPI.GetSingletonRW<SimulationDataComponent>().ValueRW;

    new MyBodiesJob { Strength = 1f }
        .ScheduleAndChain(ref state, in structures, ref simulation);
}

ScheduleAndChain combines state.Dependency and simulation.PhysicsJobHandle, schedules over ActiveBodiesCount, and writes the result back to PhysicsJobHandle.

For extra native arrays alongside body data — for example Randoms — pass them as fields on an IBodiesJob.IWriteIndex job and use ScheduleAndChain. For more complex multi-buffer access, use raw IJobParallelFor — see Custom job interfaces.

Choosing an interface

Task Interface
Per-body force, damping, or pose tweak IBodiesJob
React to object-to-object pairs or triggers ICollisionJob
Custom friction or slide along ground ISurfaceJob
Ray/segment cast against bodies in the map ILineCastJob

Limitations

  • Dynamic bodies are spheres only — mid-step shape assumptions match that constraint.
  • Object-to-object collisions only occur inside the spatial map bounds; outside the map, dynamics still get gravity and surface collision.
  • Collision slot caps come from LOD settings — not every theoretical overlap gets a slot when limits are hit.
  • Determinism decreases with loose LOD; tighter camera ranges improve repeatability.

This site uses Just the Docs, a documentation theme for Jekyll.