Floating bug with colliders. Why this may happen?

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?

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?

1 Like


I send bug. Case 1197430
And support send me link to this bug https://issuetracker.unity3d.com/issues/physics-dot-raycast-is-rarely-not-hitting-colliders-in-the-scene
I think this same bug.

Walls + static body
5179676--514220--upload_2019-11-15_21-8-55.png

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

Enemy
5179676--514232--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.

[UpdateAfter(typeof(BuildPhysicsWorld))]
    public unsafe class CollisionRaySystem : JobComponentSystem
    {
        private BuildPhysicsWorld _physicsWorld;

        private BlobAssetReference<Collider> _sphere;
        protected override void OnCreate()
        {
            _physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();

        }

        public void SetColFilter(CollisionFilter f)
        {
            _sphere = SphereCollider.Create(float3.zero, 0.25f, f);
        }

        [BurstCompile]
        private struct RayJob : IJobForEachWithEntity<SpellMayCollide, SpellVelocity, SpellPos, SpellCollision>
        {
            [ReadOnly] public PhysicsWorld w;
            [ReadOnly] public BlobAssetReference<Collider> sphere;
            public void Execute(
                Entity entity, int index,
                [ReadOnly] ref SpellMayCollide may,
                [ReadOnly] ref SpellVelocity vel,
                [ReadOnly] ref SpellPos pos,
                ref SpellCollision col
                )
            {
                var colliderCastInput = new ColliderCastInput
                {
                    Collider = (Collider*) sphere.GetUnsafePtr(),
                    Orientation = quaternion.identity,
                    Start = pos.Value,
                    End = pos.Value+vel.Value
                };

                if (w.CastCollider(colliderCastInput, out ColliderCastHit hit))
                {
                    var hitBody = w.Bodies[hit.RigidBodyIndex];
                    //col = new SpellCollision(entity, hitBody.Entity, hit);
                    col.Spell = entity;
                    col.Obstacle = hitBody.Entity;
                    col.Hit = hit;
                }
                else
                {
                     col.Spell = Entity.Null;
                    col.Obstacle = Entity.Null;
                }
            }
        }

        protected override JobHandle OnUpdate(JobHandle inputDeps)
        {
            return new RayJob
            {
                w = _physicsWorld.PhysicsWorld,
                sphere = _sphere

            }.Schedule(this,
                JobHandle.CombineDependencies(inputDeps,
                    _physicsWorld.FinalJobHandle)); //.ScheduleSingle(this, inputDeps);
        }
    }

this bonus ray system.

[UpdateAfter(typeof(BuildPhysicsWorld))]
    [UpdateAfter(typeof(CollisionRaySystem))]
    public unsafe class CollisionBonusRaySystem : ComponentSystem
    {
        private BuildPhysicsWorld _physicsWorld;
        private EntityQuery _notifyGroup;
        private BlobAssetReference<Collider> _sphere;
        private EntityArchetype _archetypeCollision;
        private EntityArchetype _archetypeBonusEffect;
        private EntityQuery _bonusGroup;
        protected override void OnCreate()
        {
            _physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();

            _notifyGroup = GetEntityQuery(
                ComponentType.ReadOnly<SpellMayCollide>(),
                ComponentType.ReadOnly<SpellVelocity>(),
                ComponentType.ReadOnly<SpellPos>()
            );
            _notifyGroup.SetFilterChanged(typeof(SpellPos));
            _archetypeCollision = EntityManager.CreateArchetype(
                typeof(BonusCollision),
                typeof(DestroyEntityOnEndTick)
            );

            _archetypeBonusEffect = EntityManager.CreateArchetype(
                typeof(TicksCooldown),
                typeof(DestroyOnTicksDone) // для удаления после кд
            );

            _bonusGroup = GetEntityQuery(ComponentType.ReadOnly<BonusOnField>());

            EntityManager.CreateEntity("StatsBonusCrystals", typeof(StatsBonusGoldCollectedSingleton));
            SetSingleton(new StatsBonusGoldCollectedSingleton(0));

        }

        public void SetColFilter(CollisionFilter f)
        {
            _sphere = SphereCollider.Create(float3.zero, 0.25f, f);
        }

        protected override void OnUpdate()
        {
            if (_notifyGroup.CalculateEntityCount() == 0) return;
            if (_bonusGroup.CalculateEntityCount() == 0) return;

            var svs = _notifyGroup.ToComponentDataArray<SpellVelocity>(Allocator.TempJob);
            var sps = _notifyGroup.ToComponentDataArray<SpellPos>(Allocator.TempJob);
            var es = _notifyGroup.ToEntityArray(Allocator.TempJob);

            //Debug.Log(svs.Length);
            for (var index = 0; index < svs.Length; index++)
            {
                var vel = svs[index];
                var pos = sps[index];

                var colliderCastInput = new ColliderCastInput
                {
                    Collider = (Collider*) _sphere.GetUnsafePtr(),
                    Orientation = quaternion.identity,
                    Start = pos.Value,                  
                    End = pos.Value+vel.Value
                };

                if (_physicsWorld.PhysicsWorld.CastCollider(colliderCastInput, out ColliderCastHit hit))
                {
                    //Debug.Log("bonus col");
                    var hitBody = _physicsWorld.PhysicsWorld.Bodies[hit.RigidBodyIndex];

                    var bonusEntity = hitBody.Entity;

                    var bonus = EntityManager.GetComponentData<BonusOnField>(bonusEntity);
                    var spell = es[index];
                    var valid = false;
                    switch (bonus.Value)
                    {
                        case BonusTypes.BlackHole:
                            //EntityManager.AddComponentData(bonusCollision.Spell, new SpellDestroy(SpellDestroySource.BlackHole));
                            if (!EntityManager.HasComponent<SpellDestroy>(spell))
                            {
                                PostUpdateCommands.AddComponent(spell, new SpellDestroy(SpellDestroySource.BlackHole));
                            }

                            //EntityManager.SetComponentData(spell, new SpellPos(bonusCollision.Pos));
                            valid = true;
                            break;

                        case BonusTypes.Fragility:
                            EntityManager.SetComponentData(spell, new SpellRefWallCount(0,1));
                            valid = true;
                            break;

                        case BonusTypes.DoubleDamage:
                            if (!EntityManager.HasComponent<SpellBonusDoubleDamage>(spell))
                            {
                                var sd = EntityManager.GetComponentData<SpellDamage>(spell);
                                sd.Damage *= 2;
                                EntityManager.SetComponentData(spell, new SpellDamage(sd));
                                PostUpdateCommands.AddComponent(spell, new SpellBonusDoubleDamage());
                                valid = true;
                            }

                            break;

                        case BonusTypes.Reflect:
                            if (!EntityManager.HasComponent<SpellBonusReflect>(spell))
                            {
                                var srpwc = EntityManager.GetComponentData<SpellRefPlayerWallCount>(spell);
                                srpwc.MaxVal += 1;
                                EntityManager.SetComponentData(spell, new SpellRefPlayerWallCount(srpwc));
                                PostUpdateCommands.AddComponent(spell, new SpellBonusReflect());
                                valid = true;
                            }

                            break;

                        case BonusTypes.Gold:
                            valid = GoldCollision(bonusEntity, bonus);
                            break;

                        case BonusTypes.Mana:
                            valid = ManaCollision(bonusEntity, bonus);
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    if (valid)
                    {
                        var e = EntityManager.CreateEntity("bonus col", _archetypeCollision);
                        EntityManager.SetComponentData(e, new BonusCollision(es[index], bonusEntity, hit.Position));
                    }
                }
            }

            svs.Dispose();
            sps.Dispose();
            es.Dispose();          
        }

        private bool GoldCollision(Entity bonusEntity, BonusOnField bonus)
        {
            EntityManager.AddComponent<DestroyEntityOnEndTick>(bonusEntity);
            Debug.Log("create gold effect");
            var e = EntityManager.CreateEntity("Gold effect", _archetypeBonusEffect);
            var bpos = EntityManager.GetComponentData<BonusWorldPos>(bonusEntity);
            EntityManager.AddComponentData(e, new BonusGoldEffect(bonus.CustomVal, bpos.Value));
            EntityManager.SetComponentData(e, new TicksCooldown(30));

            var s = GetSingleton<StatsBonusGoldCollectedSingleton>();
            SetSingleton(new StatsBonusGoldCollectedSingleton(s.Value + 1));
            return true;
        }

        private bool ManaCollision(Entity bonusEntity, BonusOnField bonus)
        {
            EntityManager.AddComponent<DestroyEntityOnEndTick>(bonusEntity);
            Debug.Log("create mana effect");
            var e = EntityManager.CreateEntity("Mana effect", _archetypeBonusEffect);
            var bpos = EntityManager.GetComponentData<BonusWorldPos>(bonusEntity);
            EntityManager.AddComponentData(e, new BonusManaEffect(bonus.CustomVal, bpos.Value));
            EntityManager.SetComponentData(e, new TicksCooldown(30));

            return true;
        }
    }

and ray from ui

private void OnSelectPointOnField(float3 p, bool isSave)
        {
            if (_curTurnPhase.Value != TurnPhases.Input) return;

            p.z = _pos.Pos.z;
            var dir = p - _pos.Pos;
            var dirn = math.normalize(dir) * 20;
            //clickPointView.position =p;
            //var ray = new Ray(_pos.Pos, dir);

            //Debug.Log("OnSelectPointOnField1");
            var rayCastInput = new ColliderCastInput
            {
                Collider = (Collider*) _sphere.GetUnsafePtr(),
                Orientation = quaternion.identity,
                Start = _pos.Pos,              
                End = _pos.Pos + dirn
            };

            if (!_physicsWorld.PhysicsWorld.CastCollider(rayCastInput, out ColliderCastHit hit))
            {
                StopDrawRay();
                return;
            }

            if (hit.Position.y <= WorldUnityObject.Instance.leftBottom.position.y)
            {
                StopDrawRay();
                return;
            }

            var startPos = _pos.Pos + (hit.Position - _pos.Pos) / 20f;

            var hitpos = hit.Position + 0.25f * hit.SurfaceNormal;
            var endPos = new Vector3(hitpos.x, hitpos.y, _pos.Pos.z);

            endTransform.position = new Vector3(endPos.x, endPos.y, -12f);
            spriteControl.transform.position = GetIntersection(startPos, endPos,
                _shieldPos - Vector3.right * 10f, _shieldPos + Vector3.right * 10f);

            endPos += Vector3.Normalize(endPos - (Vector3) startPos) * overshootDist;
            UpdateLine(startPos, endPos);

            float ang = Mathf.Atan2(endPos.y - startPos.y, endPos.x - startPos.x) * 180f / Mathf.PI;
            PlayerUnityView.Instance.OnPlayerAngleUpdate(ang);
        }

Collision filter for Enemy\Walls (read from this Gameobject and use in job for ray)
5179694--514244--upload_2019-11-15_21-23-36.png

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

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.

1 Like


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.

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?

5202110--517322--ArkanoidWars.gif

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


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

var colliderCastInput = new ColliderCastInput
{
    Collider = (Collider*) _sphere.GetUnsafePtr(),
    Orientation = quaternion.identity,
    Start = pos.Value,                  
    End = pos.Value+vel.Value
};

EnemySpawnSystem use

private PhysicsCollider _col;
private PhysicsCollider _col2x2;

And LevelSpawnBonusesSystem use colliders per bonus type

_colliders[key] = EntityManager.GetComponentData<PhysicsCollider>(pref);

And without change any logic you may change static to kinematic colliders and all work fine.