Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice
  2. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  3. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to play audio with DOTS Physics collisions?

Discussion in 'Physics Previews' started by roryo, Oct 9, 2020.

  1. roryo

    roryo

    Joined:
    May 21, 2009
    Posts:
    1,479
    I would like to have SFX play when DOTS Physics blocks collide. So far I have an event for each collision-enter, but I don't know how to play the audio from an ECS system. In the GIF below, you can see that each collision-enter event generates a console message:

    DOTSCollisionAudio.gif

    Here is the code:

    Code (CSharp):
    1. using System;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Mathematics;
    5.  
    6. [Serializable][GenerateAuthoringComponent]
    7. public struct BlockComponentData : IComponentData
    8. {
    9.     public double   hitTime;
    10.  
    11.     public float    impulse;
    12.     public float3   position;
    13.  
    14.     public bool     justEnteredCollision;
    15. }
    16.  
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using Unity.Entities;
    4. using Unity.Jobs;
    5. using Unity.Physics;
    6. using Unity.Physics.Systems;
    7.  
    8. using Unity.Mathematics;
    9. using Unity.Burst;
    10. using Unity.Collections;
    11.  
    12. public class ColliderEffectsSystem : JobComponentSystem {
    13.  
    14.     private BuildPhysicsWorld buildPhysicsWorld;
    15.     private StepPhysicsWorld stepPhysicsWorld;
    16.  
    17.  
    18.  
    19.     [BurstCompile]
    20.     [UpdateAfter(typeof(EndFramePhysicsSystem))]
    21.     private struct CollisionJob : ICollisionEventsJob {
    22.  
    23.         [ReadOnly]
    24.         public PhysicsWorld physicsWorld;
    25.  
    26.         [ReadOnly]
    27.         public double       hitTime;
    28.  
    29.         [ReadOnly]
    30.         public float        minImpulseForEffect; // seconds
    31.  
    32.         [ReadOnly]
    33.         public float        minRecollideTime; // seconds
    34.  
    35.  
    36.         public ComponentDataFromEntity<BlockComponentData> blockComponentEntities;
    37.  
    38.    
    39.  
    40.         // EXECUTE
    41.         public void Execute(CollisionEvent collisionEvent) {
    42.  
    43.             CollisionEvent.Details d = collisionEvent.CalculateDetails(ref physicsWorld);
    44.  
    45.             Entity entityA = collisionEvent.EntityA;
    46.             Entity entityB = collisionEvent.EntityB;
    47.  
    48.             if (d.EstimatedImpulse > minImpulseForEffect)
    49.             {
    50.                 if      (blockComponentEntities.HasComponent(entityA))
    51.                     ProcessCollisionEntity(entityA, d);
    52.                 else if (blockComponentEntities.HasComponent(entityB))
    53.                     ProcessCollisionEntity(entityB, d);
    54.             }
    55.         }
    56.  
    57.  
    58.  
    59.         // PROCESS COLLISION OF ENTITY
    60.         private void ProcessCollisionEntity(Entity entity, CollisionEvent.Details d)
    61.         {
    62.             if (blockComponentEntities.HasComponent(entity))
    63.             {
    64.                 BlockComponentData blockData    = blockComponentEntities[entity];
    65.  
    66.                 double timeSinceLastCollision   = hitTime - blockData.hitTime;
    67.                 blockData.hitTime = hitTime;
    68.      
    69.                 if (d.EstimatedImpulse > 1 && timeSinceLastCollision > minRecollideTime)
    70.                 {
    71.                     blockData.justEnteredCollision      = true;
    72.                     blockData.impulse                   = d.EstimatedImpulse;
    73.                     blockData.position                  = d.AverageContactPointPosition;
    74.                 }
    75.                 blockComponentEntities[entity]          = blockData;
    76.             }
    77.         }
    78.     }
    79.  
    80.  
    81.  
    82.  
    83.     protected override void OnCreate() {
    84.         buildPhysicsWorld           = World.GetOrCreateSystem<BuildPhysicsWorld>();
    85.         stepPhysicsWorld            = World.GetOrCreateSystem<StepPhysicsWorld>();
    86.     }
    87.  
    88.     protected override JobHandle OnUpdate(JobHandle inputDeps) {
    89.  
    90.         CollisionJob collisionJob   = new CollisionJob {
    91.             blockComponentEntities  = GetComponentDataFromEntity<BlockComponentData>(),
    92.             physicsWorld            = buildPhysicsWorld.PhysicsWorld,
    93.             hitTime                 = Time.ElapsedTime,
    94.             minImpulseForEffect     = 1.0f,
    95.             minRecollideTime        = 0.2f
    96.         };
    97.  
    98.         return collisionJob.Schedule(stepPhysicsWorld.Simulation, ref buildPhysicsWorld.PhysicsWorld, inputDeps);
    99.     }
    100.  
    101. }
    102.  
    103.  
    104.  
    105.  
    106. [UpdateAfter(typeof(BuildPhysicsWorld))]
    107. public class ProcessCollisionsSystem : ComponentSystem
    108. {
    109.     protected override void OnUpdate()
    110.     {
    111.         Entities.ForEach((Entity entity, ref BlockComponentData bcd) =>
    112.         {
    113.             if (bcd.justEnteredCollision)
    114.             {
    115.                 Debug.Log("JUST ENTERED COLLISION - PLAY AUDIO at: " + bcd.position);
    116.  
    117.                 bcd.justEnteredCollision = false;
    118.             }
    119.         });
    120.     }
    121. }
    122.  
    Am I on the right track here? How can I play a short AudioClip where that Debug.Log message is?

    Thanks in advance!
     
  2. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    You are on the perfect track regarding the physics stuff. Now I know nothing about audio, so it might be better to ask the question in some non-physics-related subforum, because your physics stuff is completely correct.
     
    roryo likes this.
  3. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Also make sure to take a look at CollisionEvent.Details.EstimatedContactPointPositions, the length of this array may give you the info on different audio you might want to play (1 means it's a vertex collision, 2 for edge, 3 or more for face), so you could play a different sound for each of these. At least that's what we designed it for. :)
     
  4. roryo

    roryo

    Joined:
    May 21, 2009
    Posts:
    1,479
    Thanks for this feedback, @petarmHavok! I have been burrowing down the DOTS Physics rabbit hole for a couple weeks now, so as a DOTS beginner, it is encouraging to hear that I am on the right track per physics events – or at least not going completely off the rails!

    I am excited about the vertex/edge/face collision info (which I was not aware of) not only for varying the sound files as you suggest, but also for fine tuning dust/debris particle emission. The impulse value will be used to adjust volume and pitch of the audio as well. Very exciting!

    From my research so far, it looks like DOTS Audio is in the early stages, so for now I will work on using a hybrid approach by loading and instantiating Prefabs. Here is a first pass at playing an audio source in the position of the collision contact (will add pooling next):
    Code (CSharp):
    1.  
    2. [UpdateAfter(typeof(BuildPhysicsWorld))]
    3. public class ProcessCollisionsSystem : ComponentSystem
    4. {
    5.     private GameObject stoneClunkGO;
    6.  
    7.  
    8.     protected override void OnCreate()
    9.     {
    10.         stoneClunkGO = Resources.Load("StoneClunk") as GameObject;
    11.     }
    12.  
    13.  
    14.     protected override void OnUpdate()
    15.     {
    16.         Entities.ForEach((Entity entity, ref BlockComponentData bcd) =>
    17.         {
    18.             if (bcd.justEnteredCollision)
    19.             {
    20.                 Debug.Log("JUST ENTERED COLLISION - PLAY AUDIO at: " + bcd.position);
    21.  
    22.                 // AUDIO
    23.                 GameObject clunker = GameObject.Instantiate(stoneClunkGO, bcd.position, Quaternion.identity);
    24.                 AudioSource audioSource = clunker.GetComponent<AudioSource>();
    25.                 audioSource.pitch = UnityEngine.Random.Range(0.6f, 0.9f);
    26.                 audioSource.Play();
    27.  
    28.                 bcd.justEnteredCollision = false;
    29.             }
    30.         });
    31.     }
    32. }
    33.  
    Thanks again for your help, @petarmHavok!
     
    petarmHavok likes this.
  5. AlanMattano

    AlanMattano

    Joined:
    Aug 22, 2013
    Posts:
    1,500
    I notice that the cube after collision rotates and lies on the ground for a full stop. Probably here an extra noise is needed to simulate the final corner that hits the ground. Since the collider will not detect this final collision, (because is already in a collision state) he-she can detect it by looking at the rotation. If after the collision, is still rotating and at the moment the rotation stop, then play audio. A Second softer source audion can be used.
     
    roryo and petarmHavok like this.