DOTS Unity.Physics physics engine collision event processing

 Recently, DOTS has released an official version, and has implemented a high-performance physics engine based on the DOTS concept. Today we will share with you and introduce the collision event processing and core related concepts of this physics engine.

The main process and Pipeline of Unity.Physics physics engine

 The Unity.Physics physics engine performs simulation iterative calculations mainly through the following steps:

   step1: Get the status data of our current object from the ECS component in the entity;

   step2: Do a rough broadphase calculation phase, traverse all the bodies in the physical world, and quickly determine which objects may intersect through AABB surround calculation; rough calculation, exclude those that will not intersect, and those that will not intersect will not Change the state of movement;

   step3: narrowphase stage: make further precise calculations on objects that may intersect; calculate accurate collision points and related collision information based on their physical shapes;

   step4: Based on this collision information, our physics engine will calculate specific collision information, joints, friction force, resistance, etc., combined with the principles of physics, to calculate the speed, angular velocity and other motion states of our physical rigid body.

   Step5: Based on the new motion state, all moving objects are iteratively calculated forward (linear velocity, angular velocity, friction, etc.) to calculate the position and other information of the new rigid body in this frame;

   Step6: Unity Physic synchronizes the position and velocity of the physical rigid body to the LocalTransform component and PhysicVelocity component of the node Entity through ExportPhysicsWorld System, so that the rendered entity will move synchronously with the rigid body of the physics engine;

  In DOTS, the iteration order in DOTS is determined based on the System and SystemGroup tree row structures. This is a very important concept in DOTS. Unity Physics based the above steps and logic on the ECS design idea and designed the relevant System and System Group respectively. The structure is as follows:

-->FixedStepSimulationSystemGroup:

 -->PhysicsSystemGroup

      -->PhysicsInitializeGroup(System Group)

      -->PhysicsSimulationGroup(SystemGroup)

          -->PhysicsCreateBodyPairsGroup

          -->PhysicsCreateContactsGroup

          -->PhysicsCreateJacobiansGroup

          -->PhysicsSolveAndIntegrateGroup

      -->System: ExportPhysicsWorld

The iterative calculations of all physics engines are based on FixedStepSimulationSystemGroup, which iterates physical simulations at fixed time intervals to maintain the consistency and stability of the physics engine. All simulation calculations of the physics engine are placed under the PysicsSystemGroup. PysicsSystemGroup contains PhysicsInitializeGroup, PhysicsSimulationGroup and an ExportPhysicsWorld System. Step 1 mentioned above is completed in the PhysicsInitializeGroup stage, step 2 ~ step 5 are completed in PhysicsSimulationGroup. After the PhysicsSimulationGroup is completed, the iterative calculation of one frame of the physics engine is completed. Finally, the internal data of the physics engine is synchronized to the Entity's PhysicsVelocity through the System of ExportPhysicsWorld. ECS components such as LocalTransform. There are four more subgroups in PhysicsSimulationGroup, which correspond to the execution steps of step2~step5.

Unity Physics collision detection event handling

When the group execution of PhysicsSimulationGroup is completed, the simulation and iterative calculation of the entire physics engine are completed. A PhysicsWorld will be generated during the simulation process. All rigid bodies and other related physical data (position, velocity, etc.) in the physical world can be obtained through PhysicsWorld, and finally exported to the ECS component of Entity. All events in the physical simulation will be saved in the Simulation object. These events include our common collision events and trigger events. In traditional mode, we handle these events through callback functions. In DOTS mode, we handle these events uniformly within a System link. The collision and trigger event processing flow of the physics engine is as follows:

  Step1: Write a System processing logic to handle physical events;

  Step2: Specify the timing of System execution, which must be before or after PhysicsSimulationGroup, so that the data of the collision event can be obtained;

  Step3: By writing a Job, iterate through all currently occurring collision events, and then write the processing logic for each collision event;

  Step4: Get the Simulation singleton that stores the event and pass it to the job for specific execution;

Handling of collision events:

  When all simulation iterative calculations are completed, all collision event pairs in the process will be stored in the Simulation object. We can obtain the Simulation object through (SystemBase|SystemAPI|EntityQuery).GetSingleton<SimulationSingleton>().AsSimulation().

To handle all collision events, we first write a System to write event processing logic, and then write a Job, which inherits from IcollisionEventsJob, so that all collision events can be traversed in the Job, and each collision event calls the Job's Execute function , handle the logic of each collision event in it. code show as below:

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]

[UpdateBefore(typeof(PhysicsSimulationGroup))] // We are updating before `PhysicsSimulationGroup` - this means that we will get the events of the previous frame

public partial struct GetNumCollisionEventsSystem : ISystem

{

    [BurstCompile]

    public partial struct CountNumCollisionEvents : ICollisionEventsJob

    {

        public NativeReference<int> NumCollisionEvents;

        public void Execute(CollisionEvent collisionEvent)

        {

            NumCollisionEvents.Value++;

        }

    }

    [BurstCompile]

    public void OnUpdate(ref SystemState state)

    {

        NativeReference<int> numCollisionEvents = new NativeReference<int>(0, Allocator.TempJob);

        

        state.Dependency = new CountNumCollisionEvents

        {

            NumCollisionEvents = numCollisionEvents

        }.Schedule(SystemAPI.GetSingleton<SimulationSingleton>());

        // ...

    }

}

Trigger event TriggerEvent handling:

  Trigger events are similar to collision events. We only need to write an ItriggerEventsJob to traverse all current trigger events. The code is as follows:

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]

[UpdateAfter(typeof(PhysicsSimulationGroup))] // We are updating after `PhysicsSimulationGroup` - this means that we will get the events of the current frame.

public partial struct GetNumTriggerEventsSystem : ISystem

{

    [BurstCompile]

    public partial struct CountNumTriggerEvents : ITriggerEventsJob

    {

        public NativeReference<int> NumTriggerEvents;

        public void Execute(TriggerEvent collisionEvent)

        {

            NumTriggerEvents.Value++;

        }

    }

    [BurstCompile]

    public void OnUpdate(ref SystemState state)

    {

        NativeReference<int> numTriggerEvents = new NativeReference<int>(0, Allocator.TempJob);

        

        state.Dependency = new CountNumTriggerEvents

        {

            NumTriggerEvents = numTriggerEvents

        }.Schedule(SystemAPI.GetSingleton<SimulationSingleton>());

        // ...

    }

}

That’s it for today’s sharing. Students who need the complete project tools and source code of this article can follow us.

Guess you like

Origin blog.csdn.net/Unity_RAIN/article/details/135152595