[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.
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 ...
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?)
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.
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.