Raycast for 20000 entities using Entities.ForEach

Please i need help I spent a week searching and found nothing useful !
how can i create Raycast for 20000 entities with best performance
I just need that Raycast to get Surface normals under the entities and align the entity to this normal
i’m looking for an example or any tutorial on how to raycasting from entities using Entities.ForEach
thank you !

1 Like

Have you tried looking at the Unity Physics package docs? It’s has a page on raycasting.

The only extra steps from the classic way of doing it is you need a reference to the build physics world and then the collision world from that which you provide as an argument to the raycast method. But don’t take my word for it:

https://docs.unity3d.com/Packages/com.unity.physics@0.3/manual/collision_queries.html

1 Like

thank you JakHussain !

I tried this code in JobComponentSystem since I want to take advantage of Burst Compiler but give me 2 errors :sweat_smile:
this is the code :

using Unity.Entities;
using Unity.Jobs;
using Unity.Physics;
using Unity.Collections;
using Unity.Physics.Systems;
using Unity.Transforms;
using Unity.Mathematics;


public class GameMangerDotstest : JobComponentSystem
{

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        float DeltaTime = Time.DeltaTime;
        var physicsWorldSystem = World.DefaultGameObjectInjectionWorld.GetExistingSystem<BuildPhysicsWorld>();
         var collisionWorld = physicsWorldSystem.PhysicsWorld.CollisionWorld;

        JobHandle jobhandle = Entities.WithAll<BoxTag>().ForEach((ref Translation translation, ref LocalToWorld localtoworld, ref Rotation rotation, ref PhysicsVelocity velocity) => {

            float3 RayFrom = localtoworld.Value.c3.xyz;
            float3 RayTo = RayFrom - (10 * localtoworld.Value.c1.xyz);

            RaycastInput input = new RaycastInput()
            {

                Start = RayFrom,
                End = RayTo,

                Filter = new CollisionFilter
                {
                    BelongsTo = ~0u,
                    CollidesWith = ~0u,
                    GroupIndex = 0
                }
            };

            Unity.Physics.RaycastHit hit = new Unity.Physics.RaycastHit();
            bool haveHit = collisionWorld.CastRay(input, out hit);

            if (haveHit)
            {
               
                translation.Value += 1f * localtoworld.Value.c2.xyz;
            }
            else
            {

            }
        }).Schedule(inputDeps);


        return jobhandle;

    }
}

the BoxTag is just empty IComponentData attached to a cube

when i start i got this 2 errors :

InvalidOperationException: The previously scheduled job Broadphase:PrepareStaticBodyDataJob writes to the NativeArray PrepareStaticBodyDataJob.FiltersOut. You are trying to schedule a new job GameMangerDots:<>c__DisplayClass_OnUpdate_LambdaJob0, which reads from the same NativeArray (via <>c__DisplayClass_OnUpdate_LambdaJob0.Data.collisionWorldRo.collisionWorld.Broadphase.m_StaticTree.BodyFilters). To guarantee safety, you must include Broadphase:PrepareStaticBodyDataJob as a dependency of the newly scheduled job.
Unity.Entities.JobChunkExtensions.ScheduleInternal[T] (T& jobData, Unity.Entities.EntityQuery query, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode, System.Boolean isParallel) (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/IJobChunk.cs:216)
Unity.Entities.JobChunkExtensions.ScheduleParallel[T] (T jobData, Unity.Entities.EntityQuery query, Unity.Jobs.JobHandle dependsOn) (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/IJobChunk.cs:122)
GameMangerDots.OnUpdate (Unity.Jobs.JobHandle inputDeps) (at Assets/Dots Scripts/GameMangerDots.cs:36)
Unity.Entities.JobComponentSystem.Update () (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/JobComponentSystem.cs:129)
Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ComponentSystemGroup.cs:182)
UnityEngine.Debug:LogException(Exception)
Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/Stubs/Unity/Debug.cs:19)
Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ComponentSystemGroup.cs:186)
Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ComponentSystemGroup.cs:169)
Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ComponentSystem.cs:107)
Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ScriptBehaviourUpdateOrder.cs:152)

and this :

A Native Collection has not been disposed, resulting in a memory leak. Enable Full StackTraces to get more details.

Unity Physics requires you to order your system dependencies in a certain way.

Try this:

using Unity.Entities;
using Unity.Jobs;
using Unity.Physics;
using Unity.Collections;
using Unity.Physics.Systems;
using Unity.Transforms;
using Unity.Mathematics;

[UpdateAfter(typeof(ExportPhysicsWorld)), UpdateBefore(typeof(EndFramePhysicsSystem))]
public class GameMangerDotstest : JobComponentSystem
{        
    BuildPhysicsWorld buildPhysicsWorld;
    ExportPhysicsWorld exportPhysicsWorld; 
    EndFramePhysicsSystem endFramePhysics;
    
    protected override void OnCreate()
    {
        base.OnCreate();
        buildPhysicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
        exportPhysicsWorld = World.GetOrCreateSystem<ExportPhysicsWorld>();
        endFramePhysics = World.GetOrCreateSystem<EndFramePhysicsSystem>();
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        float DeltaTime = Time.DeltaTime;
        var physicsWorld = buildPhysicsWorld.PhysicsWorld;

        inputDeps = JobHandle.CombineDependencies(inputDeps, exportPhysicsWorld.FinalJobHandle);

        JobHandle jobhandle = Entities.WithAll<BoxTag>().WithReadOnly(physicsWorld).ForEach((ref Translation translation, ref LocalToWorld localtoworld, ref Rotation rotation, ref PhysicsVelocity velocity) => 
        {

            float3 RayFrom = localtoworld.Value.c3.xyz;
            float3 RayTo = RayFrom - (10 * localtoworld.Value.c1.xyz);

            RaycastInput input = new RaycastInput()
            {

                Start = RayFrom,
                End = RayTo,

                Filter = new CollisionFilter
                {
                    BelongsTo = ~0u,
                    CollidesWith = ~0u,
                    GroupIndex = 0
                }
            };

            Unity.Physics.RaycastHit hit = new Unity.Physics.RaycastHit();
            bool haveHit = physicsWorld.CastRay(input, out hit);

            if (haveHit)
            {
            
                translation.Value += 1f * localtoworld.Value.c2.xyz;
            }
            else
            {

            }
        }).Schedule(inputDeps);

        endFramePhysics.HandlesToWaitFor.Add(jobhandle);
        return jobhandle;

    }
}
2 Likes

thank you very much desertGhost_ !
this give an error before the start :
C:\Dots game\ Assets\Dots Scripts\GameMangerDotstest.cs(31,9): error DC0034: Entities.WithReadOnly is called with an argument physicsWorld of unsupported type Unity.Physics.PhysicsWorld. It can only be called with an argument that is marked with [NativeContainerAttribute].

I just copied the code as it and replaced it with the original, should I do something more to make it work?

and please can you tell me where i can learn more about this stuff ?

@

Just remove .WithReadOnly(physicsWorld) you don’t need it there. And you can use WithReadOnly only with native containers

1 Like

Which version of entities are you using? I’m using 0.9 with SystemBase and I can use WithReadOnly with PhysicsWorld.

thank you apaxnid ! it works now
desertGhost_ yes i’m useing 0.6 thats why did not work with WithReadOnly(physicsWorld) ,will it affect anything if I don’t put it on? The code is now working fine
And does this code give a fixed timestep like FixedUpdate in MonoBehaviour ?if not how could i make it fixed step ?

Oh they changed it. Sorry for disinformation than:p

1 Like

No, this code does not give you a fixed timestep. Look at FixedRateUtils.cs for fixed timestep

1 Like

okay i will check it out thanks a lot !!

Thanks @desertGhost here’s an updated version:

    [UpdateAfter(typeof(ExportPhysicsWorld)), UpdateBefore(typeof(EndFramePhysicsSystem))]
    public class RayCastSystem : JobComponentSystem
    {       
        BuildPhysicsWorld buildPhysicsWorld;
        ExportPhysicsWorld exportPhysicsWorld;
        EndFramePhysicsSystem endFramePhysics;
      
        protected override void OnCreate()
        {
            base.OnCreate();
            buildPhysicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
            exportPhysicsWorld = World.GetOrCreateSystem<ExportPhysicsWorld>();
            endFramePhysics = World.GetOrCreateSystem<EndFramePhysicsSystem>();
        }
    
        protected override JobHandle OnUpdate(JobHandle inputDeps)
        {
            var physicsWorld = buildPhysicsWorld.PhysicsWorld;
    
            inputDeps = JobHandle.CombineDependencies(inputDeps, exportPhysicsWorld.GetOutputDependency());
    
            var jobHandle = Entities.WithReadOnly(physicsWorld).ForEach((ref LocalToWorld localToWorld, ref RayCastTag rayCast) =>
            {
                var rayFrom = localToWorld.Value.c3.xyz;
                var rayTo = rayFrom - (rayCast.Length * localToWorld.Value.c1.xyz);
    
                RaycastInput input = new RaycastInput()
                {
    
                    Start = rayFrom,
                    End = rayTo,
    
                    Filter = new CollisionFilter
                    {
                        BelongsTo = ~0u,
                        CollidesWith = ~0u,
                        GroupIndex = 0
                    }
                };

                bool haveHit = physicsWorld.CastRay(input, out _);
                if (haveHit)
                {
                    UnityEngine.Debug.Log("Hit");
                    // translation.Value += 1f * localtoworld.Value.c2.xyz;
                }
                else
                {
                    UnityEngine.Debug.Log("No Hit");
    
                }
            }).Schedule(inputDeps);
    
            endFramePhysics.AddInputDependency(jobHandle);
            return jobHandle;
        }

@elJoel do you have a working version for the latest packages? Your RayCastSystem throws for me with the latest packages:

InvalidOperationException: The previously scheduled job RaycastSystem:<>c__DisplayClass_OnUpdate_LambdaJob0 reads from the Unity.Collections.NativeArray`1[Unity.Physics.CollisionFilter] <>c__DisplayClass_OnUpdate_LambdaJob0.JobData.physicsWorld.CollisionWorld.Broadphase.m_StaticTree.BodyFilters. You are trying to schedule a new job Broadphase:PrepareStaticBodyDataJob, which writes to the same Unity.Collections.NativeArray`1[Unity.Physics.CollisionFilter] (via PrepareStaticBodyDataJob.FiltersOut). To guarantee safety, you must includ
InvalidOperationException: Adding/removing components or changing position/rotation/velocity/collider ECS data on dynamic entities during physics step

The only change you need is removing the first line:

   [UpdateAfter(typeof(ExportPhysicsWorld)), UpdateBefore(typeof(EndFramePhysicsSystem))]
1 Like