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

Question Undesired Collision Behavior Using DOTS

Discussion in 'Physics Previews' started by fomartin, Sep 5, 2020.

  1. fomartin

    fomartin

    Joined:
    Aug 31, 2015
    Posts:
    17
    Hello folks! I'm working on an archery project in DOTS to get my feet wet, and am noticing some weird behavior when my arrow collides against a wall using Unity.Physics.CollisionEvent.

    After the collision the rotation of the arrow is set incredibly weird. If I use Unity.Physics.TriggerEvent we get the desired effect, but I want to use CollisionEvent because I later want to be able to have the arrows bounce off the wall if too slow or at a an obtuse angle on collision. I have my arrow set up the following way, with PhysicsShape Material.CollisionResponse set to Collide Raise Collision Events:

    View attachment 694022

    So what I think is happening is the following:
    1. The arrow collides with the wall.
    2. Collision event is raised.
    3. Physics calculations are made on transform.
    4. Arrow is rendered with new transform.
    5. My ArrowCollisionSystem processes the collision event.
      1. CollisionJob sets the InverseInertia of the PhysicsMass to 0 to stop all velocity.
      2. AttachJob sets the arrows parent, in case the object moves, the arrow remains attached.
    If this is indeed what is happening then it means by the time we get to my ArrowCollisionSystem, the LocalToWorld component of the arrow has already been updated to a post collision transform where we are "bouncing" off the wall. Hence the weird rotation when attaching. I think this may be the case due to how the rotation of the arrow seems to correlate with the angle of the arrow to the wall on collision. If you see in the video, the more angled I shoot towards the wall, the more pronounced the rotation is. This seems to be true on all directions.

    To mitigate this, I decided to add another component to the ArrowEntity that will remember the previous frame's LocalToWorld component. This way I can hard reset the arrow rotation to the previous LocalToWorld.Rotation pre-collision. Here's the ArrowCollisionSystem right now:

    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Jobs;
    5. using Unity.Mathematics;
    6. using Unity.Physics;
    7. using Unity.Physics.Systems;
    8. using Unity.Transforms;
    9.  
    10. namespace JuegosFrantasticos
    11. {
    12.     [UpdateAfter(typeof(EndFramePhysicsSystem))]
    13.     public class ArrowCollisionSystem : SystemBase
    14.     {
    15.         private BuildPhysicsWorld _buildPhysicsWorld;
    16.         private StepPhysicsWorld _stepPhysicsWorld;
    17.         private EntityQuery _rotationFollowsLinearVelocityGroup;
    18.         private EntityCommandBufferSystem _entityCommandBufferSystem;
    19.         private EntityQuery _arrowEntityGroup;
    20.  
    21.         protected override void OnCreate()
    22.         {
    23.             _buildPhysicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
    24.             _stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    25.  
    26.             _rotationFollowsLinearVelocityGroup =
    27.                 GetEntityQuery(new EntityQueryDesc
    28.                 {
    29.                     All = new ComponentType[]
    30.                     {
    31.                         typeof(RotationFollowsLinearVelocityComponent)
    32.                     }
    33.                 });
    34.  
    35.             _entityCommandBufferSystem =
    36.                 World.GetOrCreateSystem<EntityCommandBufferSystem>();
    37.  
    38.             _arrowEntityGroup = GetEntityQuery(new EntityQueryDesc
    39.             {
    40.                 All = new ComponentType[]
    41.                 {
    42.                     typeof(ArrowPenetrationComponent),
    43.                     typeof(LocalToWorld),
    44.                 }
    45.             });
    46.         }
    47.  
    48.         protected override void OnUpdate()
    49.         {
    50.             if (_rotationFollowsLinearVelocityGroup.CalculateEntityCount() == 0)
    51.             {
    52.                 return;
    53.             }
    54.  
    55.             CollisionJob collisionJob = new CollisionJob
    56.             {
    57.                 PhysicsVelocityGroup =
    58.                     GetComponentDataFromEntity<PhysicsVelocity>(true),
    59.                 LocalToWorldGroup =
    60.                     GetComponentDataFromEntity<LocalToWorld>(),
    61.                 PhysicsMassGroup =
    62.                     GetComponentDataFromEntity<PhysicsMass>(),
    63.                 ArrowPenetrationGroup =
    64.                     GetComponentDataFromEntity<ArrowPenetrationComponent>(),
    65.                 RotationFollowsLinearVelocityGroup =
    66.                     GetComponentDataFromEntity<RotationFollowsLinearVelocityComponent>(),
    67.                 PreviousLocalToWorldGroup =
    68.                     GetComponentDataFromEntity<PreviousLocalToWorldComponent>(true),
    69.             };
    70.  
    71.             Dependency = collisionJob.Schedule(
    72.                 _stepPhysicsWorld.Simulation,
    73.                 ref _buildPhysicsWorld.PhysicsWorld,
    74.                 Dependency);
    75.  
    76.             AttachJob penetrationJob = new AttachJob
    77.             {
    78.                 EntityCommandBuffer =
    79.                     _entityCommandBufferSystem.CreateCommandBuffer().ToConcurrent(),
    80.                 ArrowPenetrationArchetypeChunk =
    81.                     GetArchetypeChunkComponentType<ArrowPenetrationComponent>(),
    82.                 EntityChunkType =
    83.                     GetArchetypeChunkEntityType()
    84.             };
    85.  
    86.             Dependency = penetrationJob.Schedule(_arrowEntityGroup, Dependency);
    87.         }
    88.  
    89.         [BurstCompile]
    90.         private struct CollisionJob : ICollisionEventsJob
    91.         {
    92.             public ComponentDataFromEntity<PhysicsMass>
    93.                 PhysicsMassGroup;
    94.             public ComponentDataFromEntity<ArrowPenetrationComponent>
    95.                 ArrowPenetrationGroup;
    96.             public ComponentDataFromEntity<RotationFollowsLinearVelocityComponent>
    97.                 RotationFollowsLinearVelocityGroup;
    98.             public ComponentDataFromEntity<LocalToWorld>
    99.                 LocalToWorldGroup;
    100.             [ReadOnly] public ComponentDataFromEntity<PreviousLocalToWorldComponent>
    101.                 PreviousLocalToWorldGroup;
    102.             [ReadOnly] public ComponentDataFromEntity<PhysicsVelocity>
    103.                 PhysicsVelocityGroup;
    104.  
    105.             public void Execute(CollisionEvent triggerEvent)
    106.             {
    107.  
    108.                 bool isEntityARotationFollowsLinearVelocity =
    109.                     RotationFollowsLinearVelocityGroup.Exists(triggerEvent.EntityA);
    110.                 bool isEntityBRotationFollowsLinearVelocity =
    111.                     RotationFollowsLinearVelocityGroup.Exists(triggerEvent.EntityB);
    112.  
    113.                 if (isEntityARotationFollowsLinearVelocity && isEntityBRotationFollowsLinearVelocity
    114.                     || !isEntityARotationFollowsLinearVelocity && !isEntityBRotationFollowsLinearVelocity)
    115.                 {
    116.                     return;
    117.                 }
    118.  
    119.                 Entity arrowEntity;
    120.                 if (isEntityARotationFollowsLinearVelocity)
    121.                 {
    122.                     arrowEntity = triggerEvent.EntityA;
    123.  
    124.                     RotationFollowsLinearVelocityGroup[arrowEntity] =
    125.                         new RotationFollowsLinearVelocityComponent() { IsActive = false };
    126.                 }
    127.                 else if (isEntityBRotationFollowsLinearVelocity)
    128.                 {
    129.                     arrowEntity = triggerEvent.EntityB;
    130.  
    131.                     RotationFollowsLinearVelocityGroup[arrowEntity] =
    132.                         new RotationFollowsLinearVelocityComponent() { IsActive = false };
    133.                 }
    134.                 else
    135.                 {
    136.                     return;
    137.                 }
    138.  
    139.                 //TODO: Remove collider or make it not throw any more events.
    140.                 if(ArrowPenetrationGroup[arrowEntity].IsPenetrated)
    141.                 {
    142.                     return;
    143.                 }
    144.  
    145.                 float4x4 debug = LocalToWorldGroup[arrowEntity].Value;
    146.  
    147.                 // Confusing collision behavior mitigation; have a new system
    148.                 // saving on to a component the arrow's previous LocalToWorld.
    149.                 // This way even if the physics engine updates the transform
    150.                 // before this collision event, we can reset it back to
    151.                 // pre-collision transform.
    152.                 // THIS DOES NOT CURRENTLY WORK
    153.                 LocalToWorldGroup[arrowEntity] =
    154.                     PreviousLocalToWorldGroup[arrowEntity].Value;
    155.  
    156.                 PhysicsMassGroup[arrowEntity] = new PhysicsMass
    157.                 {
    158.                     InverseInertia = float3.zero,
    159.  
    160.                     Transform = PhysicsMassGroup[arrowEntity].Transform,
    161.                     InverseMass = PhysicsMassGroup[arrowEntity].InverseMass,
    162.                     AngularExpansionFactor = PhysicsMassGroup[arrowEntity].AngularExpansionFactor,
    163.                     CenterOfMass = PhysicsMassGroup[arrowEntity].CenterOfMass,
    164.                     InertiaOrientation = PhysicsMassGroup[arrowEntity].InertiaOrientation,
    165.                 };
    166.  
    167.                 Entity otherEntity = !isEntityARotationFollowsLinearVelocity
    168.                     ? triggerEvent.EntityA
    169.                     : triggerEvent.EntityB;
    170.  
    171.                 ArrowPenetrationGroup[arrowEntity] =
    172.                         new ArrowPenetrationComponent(
    173.                             arrowEntity,
    174.                             LocalToWorldGroup[arrowEntity],
    175.                             otherEntity);
    176.             }
    177.         }
    178.  
    179.         [BurstCompile]
    180.         private struct AttachJob : IJobChunk
    181.         {
    182.             public EntityCommandBuffer.Concurrent EntityCommandBuffer;
    183.             public ArchetypeChunkComponentType<ArrowPenetrationComponent>
    184.                 ArrowPenetrationArchetypeChunk;
    185.  
    186.             [ReadOnly]
    187.             public ArchetypeChunkEntityType EntityChunkType;
    188.  
    189.             public void Execute(
    190.                 ArchetypeChunk chunk,
    191.                 int chunkIndex,
    192.                 int firstEntityIndex)
    193.             {
    194.                 NativeArray<Entity> entities =
    195.                     chunk.GetNativeArray(EntityChunkType);
    196.                 NativeArray<ArrowPenetrationComponent> arrowPenetrationArray =
    197.                     chunk.GetNativeArray(ArrowPenetrationArchetypeChunk);
    198.  
    199.                 for (int i = 0; i < entities.Length; ++i)
    200.                 {
    201.                     if (arrowPenetrationArray[i].IsPenetrated)
    202.                     {
    203.                         EntityCommandBuffer.AddComponent(
    204.                             chunkIndex,
    205.                             entities[i],
    206.                             new Parent
    207.                             {
    208.                                 Value = arrowPenetrationArray[i].PenetratedObject
    209.                             });
    210.  
    211.                         EntityCommandBuffer.SetComponent(
    212.                             chunkIndex,
    213.                             entities[i],
    214.                             arrowPenetrationArray[i].ArrowLocalToWorld);
    215.                     }
    216.                 }
    217.             }
    218.         }
    219.     }
    220. }
    And here's my PreviousArrowLocalToWorldUpdateSystem

    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Entities;
    3. using Unity.Jobs;
    4. using Unity.Transforms;
    5.  
    6. namespace JuegosFrantasticos
    7. {
    8.     [UpdateAfter(typeof(ArrowCollisionSystem))]
    9.     public class PreviousArrowLocalToWorldUpdateSystem : SystemBase
    10.     {
    11.         protected override void OnUpdate()
    12.         {
    13.             Entities
    14.                 .WithName("PreviousArrowLocalToWorldUpdateJob")
    15.                 .WithAll<ArrowTagComponent>()
    16.                 .ForEach(
    17.                     (ref PreviousLocalToWorldComponent previousLocalToWorld,
    18.                     in LocalToWorld localToWorld) =>
    19.                     {
    20.                         previousLocalToWorld.Value = localToWorld;
    21.                     })
    22.                 .Schedule();
    23.         }
    24.     }
    25. }
    26.  
    This still doesn't work, so I'm unsure where to go from here. I could make it a TriggerEvent again and add components that record the CollisionPoint, Impulse, etc. and then decide if the arrow penetrates or bounces off. This would then require me to do all the physics work myself in the "bounces off" case. I feel like this should be something that should eventually work out-of-the-box and is just not the case while in preview. So I may just wait until it's more baked.

    Anyways if you can think of anything else I'd love to talk more about it. Thanks! Also if you're curious how this is going and how I'm trying to get VR <> ECS working let me know and we can chat. Probs make a new thread for that though.
     
    Last edited: Sep 5, 2020
  2. fomartin

    fomartin

    Joined:
    Aug 31, 2015
    Posts:
    17
    petarmHavok likes this.