Search Unity

RaycastCommand/SpherecastCommand inside a IJobForEach?

Discussion in 'Entity Component System' started by Rotary-Heart, Jan 5, 2020.

  1. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    I'm trying to move my character controller into ECS and everything is setup correctly, but I cannot figure out how to move the RayCast logic into ECS. Here's my current approach, but it throws an error since I cannot call Schedule from inside a job.
    Code (CSharp):
    1. [BurstCompile]
    2. public struct MovementJob : IJobForEach<PhysicsMass, PhysicsVelocity, LocalToWorld, Translation, Movement, CharacterData>
    3. {
    4.    [BurstCompile]
    5.    public void Execute(ref PhysicsMass mass, ref PhysicsVelocity vel, ref LocalToWorld localToWorld, ref Translation translation, [ReadOnly] ref Movement movement, [ReadOnly] ref CharacterData characterData)
    6.    {
    7.        ...
    8.        using (NativeArray<RaycastHit> results = new NativeArray<RaycastHit>(1, Allocator.Temp))
    9.        {
    10.            NativeArray<SpherecastCommand> commands = new NativeArray<SpherecastCommand>(1, Allocator.Temp)
    11.            {
    12.                [0] = new SpherecastCommand(translation.Value, characterData.groundingRadius, -localToWorld.Up)
    13.            };
    14.  
    15.            // Schedule the batch of sphere casts
    16.            JobHandle handle = SpherecastCommand.ScheduleBatch(commands, results, 1, default(JobHandle));
    17.  
    18.            // Wait for the batch processing job to complete
    19.            handle.Complete();
    20.  
    21.            // Copy the result. If batchedHit.collider is null, there was no hit
    22.            RaycastHit batchedHit = results[0];
    23.  
    24.            if (batchedHit.collider != null)
    25.            {
    26.                //Logic goes here
    27.            }
    28.  
    29.            // Dispose the buffers
    30.            commands.Dispose();
    31.        }
    32.        ...
    33.    }
    34. }
    The only examples I've seen for RaycastCommand is using it's own job and from the System passing all the positions and data it requires. Is there any way to use this with a IJobForEach?
     
  2. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    992
    I think you are looking for this documentation https://docs.unity3d.com/Packages/com.unity.physics@0.2/manual/collision_queries.html.

    More specificaly for your case :

    Code (CSharp):
    1. public unsafe Entity SphereCast(float3 RayFrom, float3 RayTo, float radius)
    2.     {
    3.         var physicsWorldSystem = World.Active.GetExistingSystem<Unity.Physics.Systems.BuildPhysicsWorld>();
    4.         var collisionWorld = physicsWorldSystem.PhysicsWorld.CollisionWorld;
    5.  
    6.         var filter = new CollisionFilter()
    7.         {
    8.             BelongsTo = ~0u,
    9.             CollidesWith = ~0u, // all 1s, so all layers, collide with everything
    10.             GroupIndex = 0
    11.         };
    12.  
    13.         SphereGeometry sphereGeometry = new SphereGeometry() { Center = float3.zero, Radius = radius };
    14.         BlobAssetReference<Collider> sphereCollider = SphereCollider.Create(sphereGeometry, filter);
    15.  
    16.         ColliderCastInput input = new ColliderCastInput()
    17.         {
    18.             Collider = (Collider*)sphereCollider.GetUnsafePtr(),
    19.             Orientation = quaternion.identity,
    20.             Start = RayFrom,
    21.             End = RayTo
    22.         };
    23.  
    24.         ColliderCastHit hit = new ColliderCastHit();
    25.         bool haveHit = collisionWorld.CastCollider(input, out hit);
    26.         if (haveHit)
    27.         {
    28.             // see hit.Position
    29.             // see hit.SurfaceNormal
    30.             Entity e = physicsWorldSystem.PhysicsWorld.Bodies[hit.RigidBodyIndex].Entity;
    31.             return e;
    32.         }
    33.         return Entity.Null;
    34.     }
    35.  
     
    VasileTimus and Rotary-Heart like this.
  3. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Calling the collisionWorld.CastCollider throws an error:
    Code (CSharp):
    1. ArgumentException: Slice may not be used on a restricted range array
    2. Parameter name: array
    3. Unity.Collections.NativeSlice`1[T]..ctor (Unity.Collections.NativeArray`1[T] array, System.Int32 start, System.Int32 length) (at <0d01204b437e47c1a78b600b597a89f4>:0)
    4. Unity.Collections.NativeSlice`1[T]..ctor (Unity.Collections.NativeArray`1[T] array) (at <0d01204b437e47c1a78b600b597a89f4>:0)
    5. Unity.Collections.NativeSlice`1[T].op_Implicit (Unity.Collections.NativeArray`1[T] array) (at <0d01204b437e47c1a78b600b597a89f4>:0)
    6. Unity.Physics.CollisionWorld.CastCollider[T] (Unity.Physics.ColliderCastInput input, T& collector) (at Library/PackageCache/com.unity.physics@0.2.5-preview.1/Unity.Physics/Collision/World/CollisionWorld.cs:149)
    7. Unity.Physics.QueryWrappers.ColliderCast[T] (T& target, Unity.Physics.ColliderCastInput input, Unity.Physics.ColliderCastHit& result) (at Library/PackageCache/com.unity.physics@0.2.5-preview.1/Unity.Physics/Collision/Queries/Collidable.cs:134)
    8. Unity.Physics.CollisionWorld.CastCollider (Unity.Physics.ColliderCastInput input, Unity.Physics.ColliderCastHit& closestHit) (at Library/PackageCache/com.unity.physics@0.2.5-preview.1/Unity.Physics/Collision/World/CollisionWorld.cs:145)
    I didn't change anything, I just pasted your code to see if it works.
     
  4. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    I fixed this by adding a [ReadOnly] attribute to the CollisionWorld and passing the value from the system.
     
  5. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    992
    Glad it worked out. Yes this documentation sample is not jobified.
     
  6. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    Just a side note, RaycastCommand and SpherecastCommand are refer to Raycast and Spherecast within PhysX, not Unity.Physics.
     
  7. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Well I think that I found a bug or I'm misunderstanding something. I can get SphereCast to work perfectly, but RayCast from the link https://docs.unity3d.com/Packages/com.unity.physics@0.2/manual/collision_queries.html is not triggering collision when using a different mask for CollidesWith. Here's my code:

    Code (CSharp):
    1. [BurstCompile]
    2. unsafe bool SphereCast(float3 from, float3 to, float radius, out ColliderCastHit hit)
    3. {
    4.    CollisionFilter collisionFilter = new CollisionFilter()
    5.    {
    6.        BelongsTo = (1<<11),                //Character
    7.        CollidesWith = ~(uint)(1<<11),      //All but character
    8.        GroupIndex = 0
    9.    }
    10.  
    11.    SphereGeometry sphereGeometry = new SphereGeometry() { Center = float3.zero, Radius = radius };
    12.    BlobAssetReference<Unity.Physics.Collider> sphereCollider = Unity.Physics.SphereCollider.Create(sphereGeometry, collisionFilter);
    13.  
    14.    ColliderCastInput input = new ColliderCastInput()
    15.    {
    16.        Collider = (Unity.Physics.Collider*)sphereCollider.GetUnsafePtr(),
    17.        Orientation = quaternion.identity,
    18.        Start = from,
    19.        End = to
    20.    };
    21.  
    22.    return collisionWorld.CastCollider(input, out hit);
    23. }
    24.  
    25. [BurstCompile]
    26. bool RayCast(float3 from, float3 to, out Unity.Physics.RaycastHit hit)
    27. {
    28.    CollisionFilter collisionFilter = new CollisionFilter()
    29.    {
    30.        BelongsTo = (1<<11),                //Character
    31.        CollidesWith = ~(uint)(1<<11),      //All but character
    32.        GroupIndex = 0
    33.    }
    34.    
    35.    RaycastInput input = new RaycastInput()
    36.    {
    37.        Start = from,
    38.        End = to,
    39.        Filter = collisionFilter
    40.    };
    41.  
    42.    return collisionWorld.CastRay(input, out hit);
    43. }
    This when checking if grounded wouldn't work with ray cast, but it works with sphere cast. Using the exact same from and to values. If I change the mask to use
    CollidesWith = ~0u
    , like on the link, then it detects collision with the character. Am I missing something?
     
  8. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    992
    Hello, You use the same from and to values but in one case, you use a ray, in the other you used a sphere. I think you have to take into consideration the sphere radius. your sphere may be hitting something because of it's radius (schematicly, the center of the sphere did not hit anything but it's surface did).
     
  9. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Even thought that would make sense, I've tested it with a massive distance and still returns nothing. I just gave up on the idea of using the RayCast since I never found any way to make it work.
     
  10. Darkgaze

    Darkgaze

    Joined:
    Apr 3, 2017
    Posts:
    397
    It would be better to have a chain of jobs. After a job, pass the handle to the raycastCommand job, then back to another job.