How to properly schedule Raycast Job to check the ground existence

using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Extensions;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;

[UpdateBefore(typeof(TransformSystemGroup))]
public partial struct FartManMovementSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        FartManMoveInputComponent playerInput = SystemAPI.GetSingleton<FartManMoveInputComponent>();
        Debug.Log(playerInput.rotationVector);
        new FartDistanceJob { playerInput = playerInput }.Run();
        JobHandle fartJob = new FartManMoveJob { playerInput = playerInput }.Schedule(state.Dependency);
        fartJob.Complete();
        JobHandle rotateJob = new FartManRotateJob { playerInput = playerInput }.Schedule(state.Dependency);
        rotateJob.Complete();
        SystemAPI.SetSingleton(new FartManMoveInputComponent
        {
            fartPower = 0,
            fartPowerMuliplyer = SystemAPI.GetSingleton<FartManMoveInputComponent>().fartPowerMuliplyer,
            rotationVector = 0,
            rotationPower = SystemAPI.GetSingleton<FartManMoveInputComponent>().rotationPower,
            goodDistance = false
        });
    }
}

public partial struct FartManMoveJob : IJobEntity
{
    public FartManMoveInputComponent playerInput;
    [BurstCompile]
    public void Execute(BodyTagComponent body, ref PhysicsVelocity velocity, ref PhysicsMass mass, ref LocalTransform transform)
    {
        if (playerInput.fartPower > 0 && playerInput.goodDistance)
        {
            velocity.ApplyLinearImpulse(mass, transform.Up() * playerInput.fartPower);
        }
    }
}

public partial struct FartManRotateJob : IJobEntity
{
    public FartManMoveInputComponent playerInput;
    [BurstCompile]
    public void Execute(HeadTagComponent head, ref PhysicsVelocity velocity, ref PhysicsMass mass, ref LocalTransform transform)
    {
        if (playerInput.rotationVector != 0)
            velocity.ApplyLinearImpulse(mass, transform.Right() * playerInput.rotationVector * playerInput.rotationPower);   
    }
}

public partial struct FartDistanceJob : IJobEntity
{
    public FartManMoveInputComponent playerInput;
    [BurstCompile]
    public void Execute(BodyTagComponent body, ref PhysicsVelocity velocity, ref PhysicsMass mass, ref LocalTransform transform)
    {
        EntityQueryBuilder builder = new EntityQueryBuilder(Allocator.Temp).WithAll<PhysicsWorldSingleton>();
        EntityQuery singletonQuery = World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntityQuery(builder);
        var collisionWorld = singletonQuery.GetSingleton<PhysicsWorldSingleton>().CollisionWorld;
        singletonQuery.Dispose();
        RaycastInput input = new RaycastInput()
        {
            Start = transform.Position,
            End = -transform.Up() * playerInput.fartPower,
            Filter = new CollisionFilter()
            {
                BelongsTo = 3,
                CollidesWith = 6
            }
        };
        Unity.Physics.RaycastHit hit = new Unity.Physics.RaycastHit();
        if (playerInput.fartPower > 0 && collisionWorld.CastRay(input, out hit))
        {
            playerInput.goodDistance = true;
        }
    }
}

Hi everyone. I made FartDistanceJob to detect if there is ground under my player, using Unity docs.
But it throws me this exeption: InvalidOperationException: The previously scheduled job ExportPhysicsWorld:CheckDynamicBodyIntegrity reads from the ComponentTypeHandle<Unity.Physics.PhysicsVelocity> CheckDynamicBodyIntegrity.JobData.PhysicsVelocityType. You are trying to schedule a new job FartDistanceJob, which writes to the same ComponentTypeHandle<Unity.Physics.PhysicsVelocity> (via FartDistanceJob.JobData.__TypeHandle.__Unity_Physics_PhysicsVelocity_RW_ComponentTypeHandle). To guarantee safety, you must include ExportPhysicsWorld:CheckDynamicBodyIntegrity as a dependency of the newly scheduled job.

I tried schedule it like this:

JobHandle distanceJob = new FartDistanceJob { playerInput = playerInput }.Schedule(state.Dependency);
distanceJob.Complete();

But it says: InvalidOperationException:
The UNKNOWN_OBJECT_TYPE has been declared as [WriteOnly] in the job, but you are reading from it.
And
UnityException: GetOrCreateTrackerHandleImpl can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don’t use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

How can I solve this and check ground existence

You need to assign the JobHandle you receive when scheduling the job back to the state.Dependency.
Like this:

JobHandle distanceJob = new FartDistanceJob { playerInput = playerInput }.Schedule(state.Dependency);
state.Dependency = distanceJob;
distanceJob.Complete();

You should also not complete your jobs immediately in your system’s OnUpdate() function and instead chain them so that they can run in the right order when they depend on each other.
Once you are done chaining all your jobs up, you assign the final job handle to state.Dependency to properly declare the dependencies of your full system. Then the job dependency system will be able to ensure proper ordering of job executions.

Here is an example of two of your dependent jobs being chained correctly and scheduled in a non-blocking manner, meaning, they won’t get run right away but when the job systems finds a good moment to schedule them, freeing up the main thread immediately for other systems to update immediately after your system has finished.

// Pass the system's Dependency as input dependency to the job scheduling function to make sure the job is only run once its dependencies are resolved (i.e., other previously scheduled jobs that write to components it reads or reads from components it writes to have already been completed)
JobHandle handle = new FartDistanceJob { playerInput = playerInput }.Schedule(state.Dependency);

// When your jobs are run by the job system, make sure that the FartDistanceJob runs before the FartMovementJob by chaining the latter to the former.
handle = new FartMovementJob { ... }.Schedule(handle);

// Assign the final job handle from all your scheduled jobs back to the system's Dependency. This makes sure that the dependency graph is properly updated with the jobs that you are scheduling in your system, which ensures that the data you read from or write to is considered by following system's when scheduling jobs.
state.Dependency = handle;

Also, make sure not to pass components by ref in your IJobEntity.Execute functions if you don’t need to write to them. This misleads the job dependency management into assuming you are writing to these components, which can cause too restrictive job dependencies, reducing parallelism.

Finally, it appears to me that the two jobs that apply impulses can be combined into one (FartManMoveJob and FartManRotateJob). That would reduce job scheduling overhead.

Once you are done with your changes and you got this all running, make sure to use the Profiler to inspect your jobs in the timeline and make sure they are properly ordered and there are no undesired sync points and waits in your system or blocking job executions (which would be caused by manual Complete() calls and Run() calls respectively).

Thank you for reply.
But even after I made chain of jobs, merged two similar jobs, removed refs in parameters, I’m still getting this error:
InvalidOperationException: The UNKNOWN_OBJECT_TYPE has been declared as [WriteOnly] in the job, but you are reading from it.
Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckReadAndThrowNoEarlyOut (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <10871f9e312b442cb78b9b97db88fdcb>:0)
Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckReadAndThrow (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <10871f9e312b442cb78b9b97db88fdcb>:0)
Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr[T] (Unity.Collections.NativeArray1[T] nativeArray) (at <10871f9e312b442cb78b9b97db88fdcb>:0) Unity.Physics.BoundingVolumeHierarchy..ctor (Unity.Collections.NativeArray1[T] nodes, Unity.Collections.NativeArray1[T] nodeFilters) (at ./Library/PackageCache/com.unity.physics@1.0.16/Unity.Physics/Collision/Geometry/BoundingVolumeHierarchy.cs:26) Unity.Physics.Broadphase+Tree.get_BoundingVolumeHierarchy () (at ./Library/PackageCache/com.unity.physics@1.0.16/Unity.Physics/Collision/World/Broadphase.cs:374) Unity.Physics.Broadphase.CastRay[T] (Unity.Physics.RaycastInput input, Unity.Collections.NativeArray1[T] rigidBodies, T& collector) (at ./Library/PackageCache/com.unity.physics@1.0.16/Unity.Physics/Collision/World/Broadphase.cs:534)
Unity.Physics.CollisionWorld.CastRay[T] (Unity.Physics.RaycastInput input, T& collector) (at ./Library/PackageCache/com.unity.physics@1.0.16/Unity.Physics/Collision/World/CollisionWorld.cs:338)
Unity.Physics.QueryWrappers.RayCast[T] (T& target, Unity.Physics.RaycastInput input, Unity.Physics.RaycastHit& closestHit) (at ./Library/PackageCache/com.unity.physics@1.0.16/Unity.Physics/Collision/Queries/Collidable.cs:664)
Unity.Physics.CollisionWorld.CastRay (Unity.Physics.RaycastInput input, Unity.Physics.RaycastHit& closestHit) (at ./Library/PackageCache/com.unity.physics@1.0.16/Unity.Physics/Collision/World/CollisionWorld.cs:318)
FartDistanceJob.Execute (BodyPartTagComponent bodyPart, Unity.Physics.PhysicsVelocity& velocity, Unity.Physics.PhysicsMass mass, Unity.Transforms.LocalTransform transform) (at Assets/Scripts/FartMan/Movement/FartManMovementSystem.cs:77)
FartDistanceJob.Execute (Unity.Entities.ArchetypeChunk& chunk, System.Int32 chunkIndexInQuery, System.Boolean useEnabledMask, Unity.Burst.Intrinsics.v128& chunkEnabledMask) (at Unity.Entities.SourceGen.JobEntityGenerator/Unity.Entities.SourceGen.JobEntity.JobEntityGenerator/Temp/GeneratedCode/Assembly-CSharp/FartManMovementSystem__JobEntity_20953386240.g.cs:40)
FartDistanceJob.Unity.Entities.IJobChunk.Execute (Unity.Entities.ArchetypeChunk& chunk, System.Int32 unfilteredChunkIndex, System.Boolean useEnabledMask, Unity.Burst.Intrinsics.v128& chunkEnabledMask) <0x22792d1c320 + 0x00072> in :0
Unity.Entities.JobChunkExtensions+JobChunkProducer1[T].ExecuteInternal (Unity.Entities.JobChunkExtensions+JobChunkWrapper1[T]& jobWrapper, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at ./Library/PackageCache/com.unity.entities@1.0.16/Unity.Entities/IJobChunk.cs:416)
Unity.Entities.JobChunkExtensions+JobChunkProducer1[T].Execute (Unity.Entities.JobChunkExtensions+JobChunkWrapper1[T]& jobWrapper, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at ./Library/PackageCache/com.unity.entities@1.0.16/Unity.Entities/IJobChunk.cs:363)

System looks like this now:

using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Extensions;
using Unity.Transforms;
using UnityEngine;

[UpdateBefore(typeof(TransformSystemGroup))]
public partial struct FartManMovementSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        FartManMoveInputComponent playerInput = SystemAPI.GetSingleton<FartManMoveInputComponent>();
        Debug.Log(playerInput.rotationVector);
        JobHandle handle = new FartDistanceJob { playerInput = playerInput }.Schedule(state.Dependency);
        handle = new FartManMoveJob { playerInput = playerInput }.Schedule(handle);
        state.Dependency = handle;
        handle.Complete();
        SystemAPI.SetSingleton(new FartManMoveInputComponent
        {
            fartPower = 0,
            fartPowerMuliplyer = SystemAPI.GetSingleton<FartManMoveInputComponent>().fartPowerMuliplyer,
            rotationVector = 0,
            rotationPower = SystemAPI.GetSingleton<FartManMoveInputComponent>().rotationPower,
            goodDistance = false
        });
    }
}

public partial struct FartManMoveJob : IJobEntity
{
    public FartManMoveInputComponent playerInput;
    [BurstCompile]
    public void Execute(BodyPartTagComponent bodyPart, ref PhysicsVelocity velocity, PhysicsMass mass, LocalTransform transform)
    {
        Debug.Log("Execution");
        //body impulse
        if (bodyPart.part == 0 && playerInput.fartPower > 0 && playerInput.goodDistance)
            velocity.ApplyLinearImpulse(mass, transform.Up() * playerInput.fartPower);
        //head impulse
        if (bodyPart.part == 1 && playerInput.rotationVector != 0)
            velocity.ApplyLinearImpulse(mass, transform.Right() * playerInput.rotationVector * playerInput.rotationPower);
    }
}

public partial struct FartDistanceJob : IJobEntity
{
    public FartManMoveInputComponent playerInput;
    [BurstCompile]
    public void Execute(BodyPartTagComponent bodyPart, ref PhysicsVelocity velocity, PhysicsMass mass, LocalTransform transform)
    {
        if (bodyPart.part == 0)
        {
            EntityQueryBuilder builder = new EntityQueryBuilder(Allocator.Temp).WithAll<PhysicsWorldSingleton>();
            EntityQuery singletonQuery = World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntityQuery(builder);
            var collisionWorld = singletonQuery.GetSingleton<PhysicsWorldSingleton>().CollisionWorld;
            singletonQuery.Dispose();
            RaycastInput input = new RaycastInput()
            {
                Start = transform.Position,
                End = -transform.Up() * playerInput.fartPower,
                Filter = new CollisionFilter()
                {
                    BelongsTo = 3,
                    CollidesWith = 6
                }
            };
            Unity.Physics.RaycastHit hit = new Unity.Physics.RaycastHit();
            Debug.Log("Here");
            bool isHit = collisionWorld.CastRay(input, out hit);
            if (playerInput.fartPower > 0 && isHit)
            {
                playerInput.goodDistance = true;
            }
            Debug.Log("Finished");
        }
    }
}

Apparently it happens when i call collisionWorld.CastRay(input, out hit)

I think the issue is due to the way you are getting the collision world in your job.
By accessing it within your job, the dependency on the collision world is not automatically deduced which likely is the cause for your error.
Please obtain the collision world outside of your job within your systems’s OnUpdate function, just as is done in the RaycastWithCustomCollectorSystem in the Query - Custom Collector demo here:
https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/PhysicsSamples/Assets/7.%20Query/RaycastWithCustomCollectorSystem.cs

In your case, you can also pass the PhysicsWorldSingleton into your job, and mark it as [ReadOnly] as done in the RaycastWithCustomCollectorJob, so that you can perform raycasts without the framework expecting you to modify the physics world.

It works fine, no errors for now. Thank you

Excellent. I’m glad it worked.