Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Floating bug with colliders. Why this may happen?

Discussion in 'Physics for ECS' started by echeg, Nov 9, 2019.

  1. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    We found a very strange bug with missing \ incorrect colliders.
    We run game on a group of test users and with replay system we caught several strange situations

    Test case 1 (No changes in code between screenshots, 100% reproducible error)
    All colliders trigger
    Bonus \ Walls \ Monsters all have static collides. From character we create Ray (sphere collider cast)

    Ray not see one wall and some monsters

    But some monster stay visible for ray


    On second turn (monster move)
    All work fine



    If we change wall from static to kinematic (for test):


    walls detected, but some monsters not detected.

    If we remove from scene two bonuses. All works fine.



    Why this may happen?
     
  2. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    Weird. Are the walls separate bodies or part of a compound collider? What does your collision filter setup look like?
    How are you calling the collider cast? Are you just using closestHit or your own collector?
     
    echeg likes this.
  3. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
  4. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    Walls + static body
    upload_2019-11-15_21-8-55.png

    upload_2019-11-15_21-11-5.png

    Enemy
    upload_2019-11-15_21-11-42.png

    I use two different jobs for raycast. And One UI raycast. All 3 variants have same result.

    this enemy\wals ray system.
    Code (CSharp):
    1. [UpdateAfter(typeof(BuildPhysicsWorld))]
    2.     public unsafe class CollisionRaySystem : JobComponentSystem
    3.     {
    4.         private BuildPhysicsWorld _physicsWorld;
    5.  
    6.         private BlobAssetReference<Collider> _sphere;
    7.         protected override void OnCreate()
    8.         {
    9.             _physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
    10.  
    11.         }
    12.  
    13.         public void SetColFilter(CollisionFilter f)
    14.         {
    15.             _sphere = SphereCollider.Create(float3.zero, 0.25f, f);
    16.         }
    17.  
    18.         [BurstCompile]
    19.         private struct RayJob : IJobForEachWithEntity<SpellMayCollide, SpellVelocity, SpellPos, SpellCollision>
    20.         {
    21.             [ReadOnly] public PhysicsWorld w;
    22.             [ReadOnly] public BlobAssetReference<Collider> sphere;
    23.             public void Execute(
    24.                 Entity entity, int index,
    25.                 [ReadOnly] ref SpellMayCollide may,
    26.                 [ReadOnly] ref SpellVelocity vel,
    27.                 [ReadOnly] ref SpellPos pos,
    28.                 ref SpellCollision col
    29.                 )
    30.             {
    31.                 var colliderCastInput = new ColliderCastInput
    32.                 {
    33.                     Collider = (Collider*) sphere.GetUnsafePtr(),
    34.                     Orientation = quaternion.identity,
    35.                     Start = pos.Value,
    36.                     End = pos.Value+vel.Value
    37.                 };
    38.  
    39.                 if (w.CastCollider(colliderCastInput, out ColliderCastHit hit))
    40.                 {
    41.                     var hitBody = w.Bodies[hit.RigidBodyIndex];
    42.                     //col = new SpellCollision(entity, hitBody.Entity, hit);
    43.                     col.Spell = entity;
    44.                     col.Obstacle = hitBody.Entity;
    45.                     col.Hit = hit;
    46.                 }
    47.                 else
    48.                 {
    49.                      col.Spell = Entity.Null;
    50.                     col.Obstacle = Entity.Null;
    51.                 }
    52.             }
    53.         }
    54.  
    55.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    56.         {
    57.             return new RayJob
    58.             {
    59.                 w = _physicsWorld.PhysicsWorld,
    60.                 sphere = _sphere
    61.  
    62.             }.Schedule(this,
    63.                 JobHandle.CombineDependencies(inputDeps,
    64.                     _physicsWorld.FinalJobHandle)); //.ScheduleSingle(this, inputDeps);
    65.         }
    66.     }
    67.  
    this bonus ray system.
    Code (CSharp):
    1. [UpdateAfter(typeof(BuildPhysicsWorld))]
    2.     [UpdateAfter(typeof(CollisionRaySystem))]
    3.     public unsafe class CollisionBonusRaySystem : ComponentSystem
    4.     {
    5.         private BuildPhysicsWorld _physicsWorld;
    6.         private EntityQuery _notifyGroup;
    7.         private BlobAssetReference<Collider> _sphere;
    8.         private EntityArchetype _archetypeCollision;
    9.         private EntityArchetype _archetypeBonusEffect;
    10.         private EntityQuery _bonusGroup;
    11.         protected override void OnCreate()
    12.         {
    13.             _physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
    14.          
    15.             _notifyGroup = GetEntityQuery(
    16.                 ComponentType.ReadOnly<SpellMayCollide>(),
    17.                 ComponentType.ReadOnly<SpellVelocity>(),
    18.                 ComponentType.ReadOnly<SpellPos>()
    19.             );
    20.             _notifyGroup.SetFilterChanged(typeof(SpellPos));
    21.             _archetypeCollision = EntityManager.CreateArchetype(
    22.                 typeof(BonusCollision),
    23.                 typeof(DestroyEntityOnEndTick)
    24.             );
    25.          
    26.             _archetypeBonusEffect = EntityManager.CreateArchetype(
    27.                 typeof(TicksCooldown),
    28.                 typeof(DestroyOnTicksDone) // для удаления после кд
    29.             );
    30.          
    31.             _bonusGroup = GetEntityQuery(ComponentType.ReadOnly<BonusOnField>());
    32.          
    33.             EntityManager.CreateEntity("StatsBonusCrystals", typeof(StatsBonusGoldCollectedSingleton));
    34.             SetSingleton(new StatsBonusGoldCollectedSingleton(0));
    35.          
    36.         }
    37.  
    38.         public void SetColFilter(CollisionFilter f)
    39.         {
    40.             _sphere = SphereCollider.Create(float3.zero, 0.25f, f);
    41.         }
    42.      
    43.         protected override void OnUpdate()
    44.         {
    45.             if (_notifyGroup.CalculateEntityCount() == 0) return;
    46.             if (_bonusGroup.CalculateEntityCount() == 0) return;
    47.  
    48.             var svs = _notifyGroup.ToComponentDataArray<SpellVelocity>(Allocator.TempJob);
    49.             var sps = _notifyGroup.ToComponentDataArray<SpellPos>(Allocator.TempJob);
    50.             var es = _notifyGroup.ToEntityArray(Allocator.TempJob);
    51.  
    52.             //Debug.Log(svs.Length);
    53.             for (var index = 0; index < svs.Length; index++)
    54.             {
    55.                 var vel = svs[index];
    56.                 var pos = sps[index];
    57.              
    58.                 var colliderCastInput = new ColliderCastInput
    59.                 {
    60.                     Collider = (Collider*) _sphere.GetUnsafePtr(),
    61.                     Orientation = quaternion.identity,
    62.                     Start = pos.Value,                  
    63.                     End = pos.Value+vel.Value
    64.                 };
    65.              
    66.                 if (_physicsWorld.PhysicsWorld.CastCollider(colliderCastInput, out ColliderCastHit hit))
    67.                 {
    68.                     //Debug.Log("bonus col");
    69.                     var hitBody = _physicsWorld.PhysicsWorld.Bodies[hit.RigidBodyIndex];
    70.                  
    71.                     var bonusEntity = hitBody.Entity;
    72.                  
    73.                     var bonus = EntityManager.GetComponentData<BonusOnField>(bonusEntity);
    74.                     var spell = es[index];
    75.                     var valid = false;
    76.                     switch (bonus.Value)
    77.                     {
    78.                         case BonusTypes.BlackHole:
    79.                             //EntityManager.AddComponentData(bonusCollision.Spell, new SpellDestroy(SpellDestroySource.BlackHole));
    80.                             if (!EntityManager.HasComponent<SpellDestroy>(spell))
    81.                             {
    82.                                 PostUpdateCommands.AddComponent(spell, new SpellDestroy(SpellDestroySource.BlackHole));
    83.                             }
    84.  
    85.                             //EntityManager.SetComponentData(spell, new SpellPos(bonusCollision.Pos));
    86.                             valid = true;
    87.                             break;
    88.                      
    89.                         case BonusTypes.Fragility:
    90.                             EntityManager.SetComponentData(spell, new SpellRefWallCount(0,1));
    91.                             valid = true;
    92.                             break;
    93.                      
    94.                         case BonusTypes.DoubleDamage:
    95.                             if (!EntityManager.HasComponent<SpellBonusDoubleDamage>(spell))
    96.                             {
    97.                                 var sd = EntityManager.GetComponentData<SpellDamage>(spell);
    98.                                 sd.Damage *= 2;
    99.                                 EntityManager.SetComponentData(spell, new SpellDamage(sd));
    100.                                 PostUpdateCommands.AddComponent(spell, new SpellBonusDoubleDamage());
    101.                                 valid = true;
    102.                             }
    103.  
    104.                             break;
    105.                      
    106.                         case BonusTypes.Reflect:
    107.                             if (!EntityManager.HasComponent<SpellBonusReflect>(spell))
    108.                             {
    109.                                 var srpwc = EntityManager.GetComponentData<SpellRefPlayerWallCount>(spell);
    110.                                 srpwc.MaxVal += 1;
    111.                                 EntityManager.SetComponentData(spell, new SpellRefPlayerWallCount(srpwc));
    112.                                 PostUpdateCommands.AddComponent(spell, new SpellBonusReflect());
    113.                                 valid = true;
    114.                             }
    115.  
    116.                             break;
    117.                      
    118.                         case BonusTypes.Gold:
    119.                             valid = GoldCollision(bonusEntity, bonus);
    120.                             break;
    121.                      
    122.                         case BonusTypes.Mana:
    123.                             valid = ManaCollision(bonusEntity, bonus);
    124.                             break;
    125.                                              
    126.                         default:
    127.                             throw new ArgumentOutOfRangeException();
    128.                     }
    129.  
    130.                     if (valid)
    131.                     {
    132.                         var e = EntityManager.CreateEntity("bonus col", _archetypeCollision);
    133.                         EntityManager.SetComponentData(e, new BonusCollision(es[index], bonusEntity, hit.Position));
    134.                     }
    135.                 }
    136.             }
    137.  
    138.             svs.Dispose();
    139.             sps.Dispose();
    140.             es.Dispose();          
    141.         }
    142.  
    143.         private bool GoldCollision(Entity bonusEntity, BonusOnField bonus)
    144.         {
    145.             EntityManager.AddComponent<DestroyEntityOnEndTick>(bonusEntity);
    146.             Debug.Log("create gold effect");
    147.             var e = EntityManager.CreateEntity("Gold effect", _archetypeBonusEffect);
    148.             var bpos = EntityManager.GetComponentData<BonusWorldPos>(bonusEntity);
    149.             EntityManager.AddComponentData(e, new BonusGoldEffect(bonus.CustomVal, bpos.Value));
    150.             EntityManager.SetComponentData(e, new TicksCooldown(30));
    151.  
    152.             var s = GetSingleton<StatsBonusGoldCollectedSingleton>();
    153.             SetSingleton(new StatsBonusGoldCollectedSingleton(s.Value + 1));
    154.             return true;
    155.         }
    156.      
    157.         private bool ManaCollision(Entity bonusEntity, BonusOnField bonus)
    158.         {
    159.             EntityManager.AddComponent<DestroyEntityOnEndTick>(bonusEntity);
    160.             Debug.Log("create mana effect");
    161.             var e = EntityManager.CreateEntity("Mana effect", _archetypeBonusEffect);
    162.             var bpos = EntityManager.GetComponentData<BonusWorldPos>(bonusEntity);
    163.             EntityManager.AddComponentData(e, new BonusManaEffect(bonus.CustomVal, bpos.Value));
    164.             EntityManager.SetComponentData(e, new TicksCooldown(30));
    165.  
    166.             return true;
    167.         }
    168.     }
    and ray from ui
    Code (CSharp):
    1. private void OnSelectPointOnField(float3 p, bool isSave)
    2.         {
    3.             if (_curTurnPhase.Value != TurnPhases.Input) return;
    4.          
    5.             p.z = _pos.Pos.z;
    6.             var dir = p - _pos.Pos;
    7.             var dirn = math.normalize(dir) * 20;
    8.             //clickPointView.position =p;
    9.             //var ray = new Ray(_pos.Pos, dir);
    10.  
    11.             //Debug.Log("OnSelectPointOnField1");
    12.             var rayCastInput = new ColliderCastInput
    13.             {
    14.                 Collider = (Collider*) _sphere.GetUnsafePtr(),
    15.                 Orientation = quaternion.identity,
    16.                 Start = _pos.Pos,              
    17.                 End = _pos.Pos + dirn
    18.             };
    19.  
    20.             if (!_physicsWorld.PhysicsWorld.CastCollider(rayCastInput, out ColliderCastHit hit))
    21.             {
    22.                 StopDrawRay();
    23.                 return;
    24.             }
    25.  
    26.             if (hit.Position.y <= WorldUnityObject.Instance.leftBottom.position.y)
    27.             {
    28.                 StopDrawRay();
    29.                 return;
    30.             }
    31.  
    32.             var startPos = _pos.Pos + (hit.Position - _pos.Pos) / 20f;
    33.          
    34.             var hitpos = hit.Position + 0.25f * hit.SurfaceNormal;
    35.             var endPos = new Vector3(hitpos.x, hitpos.y, _pos.Pos.z);
    36.  
    37.             endTransform.position = new Vector3(endPos.x, endPos.y, -12f);
    38.             spriteControl.transform.position = GetIntersection(startPos, endPos,
    39.                 _shieldPos - Vector3.right * 10f, _shieldPos + Vector3.right * 10f);
    40.  
    41.             endPos += Vector3.Normalize(endPos - (Vector3) startPos) * overshootDist;
    42.             UpdateLine(startPos, endPos);
    43.  
    44.             float ang = Mathf.Atan2(endPos.y - startPos.y, endPos.x - startPos.x) * 180f / Mathf.PI;
    45.             PlayerUnityView.Instance.OnPlayerAngleUpdate(ang);
    46.         }
     
  5. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    Collision filter for Enemy\Walls (read from this Gameobject and use in job for ray)
    upload_2019-11-15_21-23-36.png

    Collision filter for Bonuses (read from this Gameobject and use in job for ray)
    upload_2019-11-15_21-24-15.png
     
  6. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    Just a quick note to say that I was looking at this project last night and you seem to be doing everything correctly. There is definitely something going wrong when everything is static. Hopefully, I'll be able to track down the problem later today. In the meantime, adding a Physics Body to everything and making them Kinematic is a good workaround.
     
    echeg likes this.
  7. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    Yes. We add kinematic =) And this problem gone
    But we found other bug with kinematic too ) but it is even rarer. If ExportPhysicsWorld system is On we found case then some colliders change positions(Translation) to positions(Translation) from another colliders. This sometime happen then some of colliders removed from scene (monster die)
    In our case, we disable ExportPhysicsWorld and StepPhysicsWorld because we use physics only for raycasts.
     
  8. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    I can't for the life of me work out what is going on here. I've created a simple repo and everything works fine for me. I added my own sphere cast and it works until the Gold Bonuses are added to the world (see attached animated gif).
    I tried adding the RayTrace code from the samples, however, if I enable it immediately all the debug display NativeStreams are Disposed by something, so accessing them throws exceptions. If I start the game and then enabled the RayTracer then it 'works', though the casts don't find anything.
    I've not familiar with the game logic. Is there any logic in the game that could be Disposing systems?
    Are you copying PhysicsColliders by value anywhere?
     

    Attached Files:

  9. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    In the animated gif the green line up from the red box at the bottom is a sphere cast that has the same properties as the games UI raycasting.You can see that one frame after the Gold Bonus is added it no longer finds a hit against the top wall
     
  10. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    You may see "public class GameController : MonoBehaviour"
    We manually run systems by ticks. "void Execute()"
    May this had effect?

    In CollisionBonusRaySystem we create one collider _sphere
    And use it for raycasts

    Code (CSharp):
    1. var colliderCastInput = new ColliderCastInput
    2. {
    3.     Collider = (Collider*) _sphere.GetUnsafePtr(),
    4.     Orientation = quaternion.identity,
    5.     Start = pos.Value,                  
    6.     End = pos.Value+vel.Value
    7. };
    EnemySpawnSystem use
    Code (CSharp):
    1. private PhysicsCollider _col;
    2. private PhysicsCollider _col2x2;
    And LevelSpawnBonusesSystem use colliders per bonus type
    Code (CSharp):
    1. _colliders[key] = EntityManager.GetComponentData<PhysicsCollider>(pref);
     
  11. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    And without change any logic you may change static to kinematic colliders and all work fine.