How to call a static method inside a Job?

    [BurstCompile]
    private struct CollisionEventJob : ICollisionEventsJob
    {
        [ReadOnly] public PhysicsWorld physicsWorld;
        [ReadOnly] public ComponentDataFromEntity<BottomBarrierComponent> bottomBarrier;
        public ComponentDataFromEntity<BallComponent> ball;

        public void Execute(CollisionEvent collisionEvent)
        {
            CollisionEvent.Details collisionDetails = collisionEvent.CalculateDetails(ref physicsWorld);
            if (collisionEvent.EntityB != ball[collisionEvent.EntityA].lastContact)
            {
                if (bottomBarrier.HasComponent(collisionEvent.EntityB))
                {
                    GameManager.EndGame();
                }
            }
        }
    }

GameManager.EndGame() makes setActive the restart button. But it throws the following error:

UnityException: SetActive 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.
GameManager.EndGame () (at Assets/Scripts/GameManager.cs:84)
BallCollisionSystem+CollisionEventJob.Execute (Unity.Physics.CollisionEvent collisionEvent) (at Assets/Scripts/Systems/BallCollisionSystem.cs:51)
Unity.Physics.ICollisionEventJobExtensions+CollisionEventJobProcess`1[T].Execute (Unity.Physics.ICollisionEventJobExtensions+CollisionEventJobData`1[T]& jobData, System.IntPtr additionalData, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at Library/PackageCache/com.unity.physics@0.51.1-preview.21/Unity.Physics/Dynamics/Simulation/ICollisionEventsJob.cs:117)

Well, you called a static method alright. That isn’t the issue. :wink:

But you cannot use managed objects from a Job, that includes whatever you do in methods that are being called from a job. GameObject.SetActive() is using managed objects.

Instead, you need to register that “endgame” as a flag in your job, such as setting a NativeReference field and after calling the job’s JobHandle.Complete() you can access that bool and based upon its value you’d call GameManager.EndGame() from the main thread.

    [BurstCompile]
    private struct CollisionEventJob : ICollisionEventsJob
    {
        [ReadOnly] public PhysicsWorld physicsWorld;
        [ReadOnly] public ComponentDataFromEntity<BottomBarrierComponent> bottomBarrier;
        public ComponentDataFromEntity<BallComponent> ball;
       [WriteOnly] public NativeReference<bool> shouldEndGame;

        public void Execute(CollisionEvent collisionEvent)
        {
            CollisionEvent.Details collisionDetails = collisionEvent.CalculateDetails(ref physicsWorld);
            if (collisionEvent.EntityB != ball[collisionEvent.EntityA].lastContact)
            {
                if (bottomBarrier.HasComponent(collisionEvent.EntityB))
                {
                    shouldEndGame.Value = true;
                }
            }
        }
    }

And on the main thread:

var job = new CollisionEventJob() { /* init stuff here */ };
job.Schedule.Complete();
if (job.shouldEndGame.Value)
    GameManager.EndGame();

// PS: don't forget to new and dispose job's NativeReference ...

I’m sorry, I’m not that well versed in DOTS yet. I can’t get access to shouldEndGame:

    protected override void OnUpdate()
    {
        ballComponent = GetComponentDataFromEntity<BallComponent>(false);
        bottomBarrierComponent= GetComponentDataFromEntity<BottomBarrierComponent>(false);

        JobHandle job = new CollisionEventJob()
        {
            ball = ballComponent,
            bottomBarrier=bottomBarrierComponent,
            shouldEndGame = new NativeReference<bool>()
           
        }.Schedule(stepPhysicsWorldSystem.Simulation, this.Dependency);
        job.Complete();
        if (job.shouldEndGame.Value)
            GameManager.EndGame();
    }

Gives an error message:
Assets\Scripts\Systems\BallCollisionSystem.cs(108,17): error CS1061: ‘JobHandle’ does not contain a definition for ‘shouldEndGame’ and no accessible extension method ‘shouldEndGame’ accepting a first argument of type ‘JobHandle’ could be found (are you missing a using directive or an assembly reference?)

This should work:

protected override void OnUpdate()
{
    ballComponent = GetComponentDataFromEntity<BallComponent>(false);
    bottomBarrierComponent= GetComponentDataFromEntity<BottomBarrierComponent>(false);

    var job = new CollisionEventJob()
    {
        ball = ballComponent,
        bottomBarrier=bottomBarrierComponent,
        shouldEndGame = new NativeReference<bool>()
    
    };
    job.Schedule(stepPhysicsWorldSystem.Simulation, this.Dependency).Complete();
 
    var shouldEndGame = job.shouldEndGame.Value
    job.shouldEndGame.Dispose();

    if (shouldEndGame)
        GameManager.EndGame();
}

You were using a JobHandle, not the job itself, because you called Schedule on the same line as creating the job instance - which means you have no access to the job instance itself, just the handle. And just to be sure, I added disposing the reference.

I split this into creating the job, then Schedule + Complete(), then the job variable has an instance of CollisionEventJob rather than JobHandle.

It throws an error in line with Schedule().Complete()

InvalidOperationException: The UNKNOWN_OBJECT_TYPE CollisionEventJob.UserJobData.shouldEndGame has not been assigned or constructed. All containers must be valid when scheduling a job.

Hmmm I think NativeReference needs at least one parameter to initialize it. Try changing that line to this:

 shouldEndGame = new NativeReference<bool>(Allocator.TempJob)
2 Likes

Yes, everything is working now. Thank you very much for the help)

You can access managed data from job, but that job can’t be bursted. And in case of GameObject.SetActive() the issue is not that it is managed, but that it’s not thread safe and has protection from unity engine side. You’ll have same error if you’ll try to SetActive from your manual threads even without job system.

2 Likes