EntityComponentSystemSamples icon indicating copy to clipboard operation
EntityComponentSystemSamples copied to clipboard

Physics stateful events inconsistency when both objects have physics body.

Open baznzab opened this issue 2 years ago • 3 comments

I found out that in case if both interacting objects have colliders and only one body has physics shape everything goes fine and trigger events are correct, but in case if both have rigidbodies at the same time instead the behaviour is chaotic.

Example

using Unity.Entities;
using Unity.Physics.Stateful;

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateAfter(typeof(StatefulTriggerEventBufferSystem))]
public partial class PickupRangeSystem : SystemBase {
  private BeginFixedStepSimulationEntityCommandBufferSystem _ecbBeginSystem;
  private EndFixedStepSimulationEntityCommandBufferSystem _ecbEndSystem;

  protected override void OnCreate() {
    base.OnCreate();
    _ecbBeginSystem = World.GetOrCreateSystem<BeginFixedStepSimulationEntityCommandBufferSystem>();
    _ecbEndSystem = World.GetOrCreateSystem<EndFixedStepSimulationEntityCommandBufferSystem>();
  }

  protected override void OnUpdate() {
    var ecbBegin = _ecbBeginSystem.CreateCommandBuffer().AsParallelWriter();
    var ecbEnd = _ecbEndSystem.CreateCommandBuffer().AsParallelWriter();

    Dependency = Entities
      .WithAll<Player>()
      .ForEach((Entity entity, int entityInQueryIndex, in DynamicBuffer<StatefulTriggerEvent> triggerEvents) => {
        foreach (var triggerEvent in triggerEvents) {
          if (!triggerEvent.TryGetOther(entity, out var other)) {
            continue;
          }
          if (triggerEvent.State == StatefulEventState.Enter) {
            ecbBegin.AddComponent(entityInQueryIndex, other.Entity, new PickupRangeEvent { State = PickupRangeState.Enter });
          } else if (triggerEvent.State == StatefulEventState.Exit) {
            ecbBegin.AddComponent(entityInQueryIndex, other.Entity, new PickupRangeEvent { State = PickupRangeState.Exit });
          }
        }
      }).ScheduleParallel(Dependency);

    Dependency = Entities
      .WithAll<PickupRangeEvent>()
      .ForEach((Entity entity, int entityInQueryIndex) => ecbEnd.RemoveComponent<PickupRangeEvent>(entityInQueryIndex, entity))
      .ScheduleParallel(Dependency);

    _ecbBeginSystem.AddJobHandleForProducer(Dependency);
    _ecbEndSystem.AddJobHandleForProducer(Dependency);
  }
}

In this example I'm using TryGetOther method i added to StatefulTriggerEvent for my convinience

public bool TryGetOther(in Entity entity, out (Entity Entity, int BodyIndex, ColliderKey ColliderKey) other) {
      if (EntityA.CompareTo(entity) != 0 && EntityB.CompareTo(entity) != 0) {
        other = (Entity.Null, -1, ColliderKey.Empty);
        return false;
      }
      other = EntityA.CompareTo(entity) == 0
        ? (EntityB, BodyIndexB, ColliderKeyB)
        : (EntityA, BodyIndexA, ColliderKeyA);
      return true;
    }

After running PickupViewSystem I'm continuously getting Enter and Exit events while bodies are colliding.

using Unity.Entities;

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
public partial class PickupViewSystem : SystemBase {
  protected override void OnUpdate() {
    Entities
      .WithoutBurst()
      .WithAll<Pickup>()
      .ForEach((in PickupRangeEvent pickupRangeEvent) => {
        UnityEngine.Debug.Log(pickupRangeEvent.State);
      }).Run();
  }
}

Haven't tested for StatefulCollisionEvent but i assume and it's quite obvious the issue exists for both.

baznzab avatar Jul 20 '22 09:07 baznzab