Use DOTS Physics for Collisions
Code and workflows to update collisions that change material and destroy prefab entities using DOTS Physics
Last updated
Code and workflows to update collisions that change material and destroy prefab entities using DOTS Physics
Last updated
We will update our project so that bullet collisions cause the material of the colliding entity to change and for the entity to be destroyed
Github branch link: https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Updating-Bullets-to-Destroy
We have mostly used .ForEach() and Job.WithCode() in this project. At their core, both of these interfaces run "jobs." When we implement our trigger events for bullet collisions we will also be using jobs, but some of the code will be using the core job interface. In some of the files we will be examining you can see the "job" struct.
Open the downloaded EntityComponentSystemSamples repo and navigate to Demos > 2. Setup > 2d. Events > Scripts > Stateful and Demos > 2. Setup > 2d. Events > 2d1. Triggers > Scripts
There are 6 files that we are going to base our bullet interactions on
in /2d. Events/Scripts/Stateful
1. IStatefulSimulationEvent.cs
2. StatefulSimulationEventBuffers.cs
3. StatefulTriggerEvent.cs
4. StatefulTriggerEventBufferAuthoring.cs
5. StatefulTriggerEventBufferSystem.cs
in /2d. Events/2d1. Triggers/Scripts
6. TriggerVolumeChangeMaterialAuthoring.cs
The first 5 files purpose is to take DOTS Physics TriggerEvents (which are stateless) and convert them to StatefulTriggerEvents. "Stateless" means that Unity Physics doesn't know what happened "before" ("before" = the previous "state"). So when 2 Physics bodies interact, there is no iterface to know if they "just" interacted this frame, or if they have already interacted.
Classic MonoBehavior physics provided stateful methods like "OnTriggerEnter" and "OnTriggerStay" and "OnTriggerExit". The first 5 files in /2d. Events/Scripts/Stateful job is to provide the same type of functionality when using DOTS Physics. The ability to know when an interaction is Enter/Stay/Exit.
The states are defined in IStatefulSimulationEvent.cs as an enum "StatefulEventState"
Undefined
Enter
Stay
Exit
StatefulTriggerEvent.cs defines the new StatefulTriggerEvent interface which is very similar to TriggerEvent but with the addition of the StatefulEventState
StatefulTriggerEventBufferAuthoring.cs is used to for Authoring and is meant to be placed on prefabs to provide it a component that can hold a buffer of StatefulTriggerEvents
StatefulSimulationEventBuffers.cs provides jobs used by StatefulTriggerEventBufferSystem.cs to convert TriggerEvents into StatefulEventStates and place them in the buffer provided by StatefulTriggerEventBufferAuthoring.cs
TriggerVolumeChangeMaterialAuthoring.cs uses StatefulTriggerEvents on the prefabs to change materials based on interactions
Let's take a deeper look into StatefulTriggerEventBufferSystem.cs to understand a bit more about Dependencies and scheduling. Unity Physics Upgrade guide for 0.7 can help us understand what is going on.
RegisterPhysicsRuntimeSystemReadOnly()
andRegisterPhysicsRuntimeSystemReadWrite()
(both registered as extensions ofSystemBase
) should be used to manage physics data dependencies instead of the oldAddInputDependency()
andGetOutputDependency()
approach. Users should declare theirUpdateBefore
andUpdateAfter
systems as before and additionally only call one of the two new functions in their system'sOnStartRunning()
, which will be enough to get an automatic update of theDependency
property without the need for manually combining the dependencies as before. Note that things have not changed if you want to read or write physics runtime data directly in your system'sOnUpdate()
- in that case, you still need to ensure that jobs from previous systems touching physics runtime data are complete, by completing theDependency
. Also note thatBuildPhysicsWorld.AddInputDependencyToComplete()
still remains needed for jobs that need to finish before any changes are made to the PhysicsWorld, if your system is not scheduled in between other 2 physics systems that will do that for you.
StatefulTriggerEventBufferSystem
This is the system that takes the intermediate results of the Solver (which are stateless) and assigns them state (enter, stay, and exit)
You can see that (following the instructions from the v0.7 upgrade guide) we are declaring when this system should be run, after running the StepPhysicsWorld and before EndFramePhysicsSystem
We also call RegisterPhysicsRuntimeSystemReadOnly() as described in the upgrade guide
We can see in StatefulTriggerEventBufferSystem's OnUpdate() that we also follow the note from the v0.7 upgrade guide that "if you want to read or write physics runtime data directly in your system's OnUpdate()
- in that case, you still need to ensure that jobs from previous systems touching physics runtime data are complete, by completing the Dependency
"
We schedule a StatefulEventCollectionJobs.CollectTriggerEvents job (which itself we define as dependent on StepPhysicsWorld.Simulation) and add the resulting dependency to StatefulTriggerEventBufferSystem's OnUpdate() job
That means this system is dependent on the results of that system to perform its operations
This makes sense because we need to grab the results of the Solver (which occurs during StepPhysicsWorld.Simulation) to get the trigger events
Otherwise we would be trying to convert TriggerEvents to StatefulTriggerEvents before the TriggerEvents have been calculated (no good! can't convert something that doesn't exist yet!)
You can see the requirements for for TriggerEvents to be transformed to StatefulTriggerEvents and stored in a Dynamic Buffer at the top of StatefulSimulationEventBuffers.cs
1) 'Raise Trigger Events' option of the 'Collision Response' on PhysicsShapeAuthoring on the entity that should raise trigger events
2) Add a StatefulTriggerEventBufferAuthoring component to that entity
3) If this is desired on a Character Controller, tick "RaiseTriggerEvents" on CharacterControllerAuthoring (and skip (1) and (2))
We already chose "Raise Trigger Events" on our bullet prefab so we are set there, and we will add StatefulTriggerEventBufferAuthoring on the bullet
Now take a look at TriggerVolumeChangeMaterialAuthoring.cs
This file also has an implementation of the IConvertGameObjectToEntity interface
This adds the "TriggerVolumeChangeMaterial" Component (defined at the top of the file) to the converted GameObject
It will grab whatever GameObject we drag onto the public field "ReferenceGameObject" in TriggerVolumeChangeMaterialAuthoring and set it as the ReferenceEntity field of the TriggerVolumeChangeMaterial Component
Or, if the public field is left null, IConvertGameObjectToEntity will set the reference entity as the GameObject too
This ReferenceEntity is used further down to reset the material to the same material as the ReferenceEntity
Let's keep moving down the file...
TriggerVolumeChangeMaterialSystem
There are a couple of decorators at the top of the file
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
FixedStepSimulationSystemGroup is the system group we reviewed in the previous section
[UpdateAfter(typeof(StatefulTriggerEventBufferSystem))]
StatefulTriggerEventBufferSystemis the system from the previous file we just reviewed
This makes sense because we want to perform our actions in the simulation and after we have created our stateful triggers
This makes sense because we need the stateful triggers produced by TriggerEventConversionSystem to exist if we want to act on them
Now, in the OnUpdate() is where we find the code that causes the material to change
By taking a look at the code that runs when shapes stop intersecting, you can see the purpose of the ReferenceEntity field
The ReferenceEntity's RenderMesh is used
Once the intersection stops, the RenderMesh is set equal to the ReferenceEntity's RenderMesh
This is why the ball turns back to its original color in the sample
We are going to update this code so that when a bullet first intersects with any entity, the other entity will have its material changed to the same material as the bullet
"On exit" we will add a DestroyTag to the entity
We do not need the "ReferenceEntity" because we will not be changing material back on exit. Instead, we will be able to remove TriggerVolumeChangeMaterial and TriggerChangeMaterialAndDestroyAuthoring
Copy and paste the 5 files from /2d. Events/Scripts/Stateful to /ScriptsAndPrefabs
1. IStatefulSimulationEvent.cs
2. StatefulSimulationEventBuffers.cs
3. StatefulTriggerEvent.cs
4. StatefulTriggerEventBufferAuthoring.cs
5. StatefulTriggerEventBufferSystem.cs
as well as 3 additional files (these are used for CollisionEvents which we are not concerned with but are also referenced by our systems so they are necessary)
6. StatefulCollisionEvent.cs
7. StatefulCollisionEventBufferAuthoring.cs
8. StatefulCollisionEventBufferSystem.cs
Now let's make some modifications to TriggerVolumeChangeMaterialAuthoring.cs so that bullets change the color of the Asteroid when they intersect and that the Asteroid is given a DestroyTag when the bullet exists.
Create ChangeMaterialAndDestroySystem and paste the code snippet below into ChangeMaterialAndDestroySystem.cs:
Next, open the Bullet prefab and add StatefulTriggerEventBufferAuthoring
Navigate to SampleScene, reimport ConvertedSubScene
Hit "play" and shoot around
Woo hoo! We have a working collision trigger system 👍
In our testing we found that occasionally it is necessary to "Reimport All" assets for the changes to take (go to "Assets" then select "Reimport All")
The changing of material is... underwhelming
Let's update the Bullet prefab to have a Red material
In the Asset folder, right-click, choose "Create", select "Material" and name it "Red"
Select the Red material, go to Base Map in the Inspector, and change it to a red color and save
Select the Bullet prefab and change the Mesh Renderer material to Red
Hit save, navigate to SampleScene, and reimport the ConvertedSubScene
Now hit "play" and checkout the difference
Now it's a bit more exciting, right?! 😬
Remember, the purpose of this gitbook is to give a "how" of using Unity's DOTS packages, not to make an exciting game 🥺
We now know how to make collisions using DOTS Physics
We navigated through DOTS Physics samples to find a sample that matched our needs
We checked out the sample scene to get an idea of the different components
We read through Physics Samples to get an idea of what changes we wanted to make
We copied 8 files needed to create StatefulEventTriggers and created ChangeMaterialAndDestroySystem in our project
We added the StatefulTriggerEventBufferAuthoring to our Bullet prefab and updated the prefab to be red
Github branch link:
git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/
git checkout 'Updating-Bullets-to-Destroy'
With these 8 files we have what we need to create StatefulEventTriggers on our prefabs