You are getting a collision world from the buildPhysicsWorld system immediately OnUpdate(), where you don’t have the guarantee that the collision world has been built. You need a dependency to the end of the buildPhysicsWorld, not the endFramePhysicsSystem.
Not sure that’s the only issue with this, but certainly is one. BuildPhysicsWorld is the one preparing the hierarchy for you to be able to perform ray casts.
How do I actually do this? I may not use the buildPhysicsWorld within the lambda (does not compile) and just adding th dependency to the buildPhysicsWorld.GetOutputDependency() does to my understanding only change when the system is run, but not when the CollisionWorld is captured, isn’t it like that?
Your comment in code says that it works if you put a Dependency.Complete() in there. Is that correct?
One other thing you need to do for sure is to inform at least one system of this dependency. If you have game logic that should happen before physics, you should inform StepPhysicsWorld. If you want to go any time in the frame, inform the EndFramePhysicsSystem. Just use the AddInputDependency() call. That should help get rid of the Complete() call.
To answer your question, it’s fine to capture it like this, since it’s all pointers internally, but if you immediately execute ray cast it will fail because you are not waiting for jobs that actually fill these pointers with useful data for ray casting.
No, but it will crash without it, as the request potentially runs over 4 frames, and then a new physics world seems to be build and Unity will complain with one of those:
Allocations older than 4 frames
Access to System BuildPhysicsWorld before … (actually it happens afterward, just in frame N+1, after we started in frame N, but before the BuildPhysicsWorld of frame N+1)
Thank you very much for the input, I will try it now without schedule but with the AddInputDependency.
// Dependency.Complete(); // <- Require or will fail
endFramePhysicsSystem.AddInputDependency(Dependency);
}
Edit… it crashes, sadly the error message is truncated (both in-ediitor, but also in the Editor.log)
InvalidOperationException: The previously scheduled job RaycastSystem:<>c__DisplayClass_RaycastSystem reads from the Unity.Collections.NativeArray`1[Unity.Physics.CollisionFilter] <>c__DisplayClass_RaycastSystem.JobData.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 include RaycastSystem:<>c__Disp
InvalidOperationException: Adding/removing components or changing position/rotation/velocity/collider ECS data on dynamic entities during physics step
Also the second error message is super weird. Why does this system modify position, rotation or velocity?! that makes no sense at all, while the first error is pretty much the same as this one , which I got stuck with in June already (never managed to properly solve it…).
The first error message is surprising to me, but just in case, could you link to the StepPhysicsWorld instead of EndFramePhysicsSystem, just to see what happens then?
The second one means that you’ve written to the component data of an entity involved in the physics step between the BuildPhysicsWorld and ExportPhysicsWorld, and that is not allowed. Even the write to a non-physics component data can result in reorder of entities in chunks, and Build and Export rely on the same order.
Yes, but where do I write when all I do is collisionWorld.Raycast? The only other system I have is one that expliticly marks entities as “this one existed before BuildPhysicsWorld” as otherwise the raycast might run a frame too early
I even create a very stupid test raycast now on the main thread, which does also not hit anything
Without any system, just to get anything working…
Button.onClick.AddListener(AddRaycast);
}
private void AddRaycast()
{
UnityEngine.Ray ray = camera.ScreenPointToRay(Input.mousePosition);
Entity raycastForward = entityManager.CreateEntity();
var input = new RaycastInput {
Start = ray.origin,
End = ray.origin + ray.direction * 1e15f
};
Debug.DrawLine(input.Start, input.End, Color.cyan, 3f);
var raycastForwardRequest = new RaycastRequest {
Completed = false,
Hit = false,
RaycastInput = input,
ConsideredBeforeBuildPhysicsWorld = false,
RaycastResult = default,
Entity = raycastForward
};
raycastRequests.Add(raycastForwardRequest);
entityManager.AddComponent<RaycastRequest>(raycastForward);
entityManager.SetComponentData(raycastForward, raycastForwardRequest);
entityManager.SetName(raycastForward, $"Raycast {input.Start}-{input.End}");
Entity raycastReverse = entityManager.CreateEntity();
var inputReverse = new RaycastInput {
Start = input.End,
End = input.Start
};
Debug.DrawLine(input.End, input.Start, Color.magenta, 4f);
var raycastReverseRequest = new RaycastRequest {
Completed = false,
Hit = false,
RaycastInput = inputReverse,
ConsideredBeforeBuildPhysicsWorld = false,
RaycastResult = default,
Entity = raycastReverse
};
raycastRequests.Add(raycastReverseRequest);
entityManager.AddComponent<RaycastRequest>(raycastReverse);
entityManager.SetComponentData(raycastReverse, raycastReverseRequest);
Entity plane = entityManager.CreateEntity();
entityManager.SetName(plane, "Plane"+(planeCount++).ToString());
entityManager.AddComponent<PhysicsCollider>(plane);
var naPos = new NativeArray<float3>(4, Allocator.Temp);
naPos[0] = new float3(-1, 0, -1);
naPos[1] = new float3(1, 0, -1);
naPos[2] = new float3(-1, 0, 1);
naPos[3] = new float3(1, 0, 1);
var naIndices = new NativeArray<int3>(2, Allocator.Temp);
naIndices[0] = new int3(0,1,2);
naIndices[1] = new int3(1,2,3);
BlobAssetReference<Unity.Physics.Collider> planeCollider = Unity.Physics.MeshCollider.Create(naPos, naIndices, CollisionFilter.Default);
entityManager.SetComponentData(plane,new PhysicsCollider {
Value = planeCollider
});
var go = new GameObject();
go.name = "WhereIsMyPlane "+planeCount;
var mesh = new Mesh {
vertices = new [] {
new Vector3(naPos[0].x,naPos[0].y,naPos[0].z),
new Vector3(naPos[1].x,naPos[1].y,naPos[1].z),
new Vector3(naPos[2].x,naPos[2].y,naPos[2].z),
new Vector3(naPos[3].x,naPos[3].y,naPos[3].z)
},
triangles = new[] {0, 2, 1, 2, 3, 1}
};
var mf = go.AddComponent<MeshFilter>();
mf.mesh = mesh;
var mr = go.AddComponent<MeshRenderer>();
mr.material = material;
var mc = go.AddComponent<MeshCollider>();
mc.sharedMesh = mesh;
naIndices.Dispose();
naPos.Dispose();
var physicsWorldSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<BuildPhysicsWorld>();
physicsWorldSystem.GetOutputDependency().Complete();
CollisionWorld collisionWorld = physicsWorldSystem.PhysicsWorld.CollisionWorld;
Debug.DrawLine(new float3(0, 1, 0), new float3(0, -1, 0), Color.red, 4f);
if (collisionWorld.CastRay(new RaycastInput {
Start = new float3(0, 1, 0),
End = new float3(0, -1, 0),
Filter = CollisionFilter.Default
}, out Unity.Physics.RaycastHit closestHit))
Debug.Log($"plane hit forward (true):{closestHit.Fraction>0}, entity:{closestHit.Entity}, {entityManager.GetName(closestHit.Entity)} position:{closestHit.Position}");
else
Debug.Log($"plane hit forward (false):{closestHit.Fraction>0}, entity:{closestHit.Entity}, position:{closestHit.Position}");
Debug.DrawLine(new float3(0, 1, 0), new float3(0, -1, 0), Color.blue, 4f);
if (collisionWorld.CastRay(new RaycastInput {
Start = new float3(0, -1, 0),
End = new float3(0, 1, 0),
Filter = CollisionFilter.Default
}, out closestHit))
Debug.Log($"plane hit backward (true):{closestHit.Fraction>0}, entity:{closestHit.Entity}, {entityManager.GetName(closestHit.Entity)} position:{closestHit.Position}");
else
Debug.Log($"plane hit backward (false):{closestHit.Fraction>0}, entity:{closestHit.Entity}, position:{closestHit.Position}");
}
I am in the process of moving this out to an example project that I am going to share, as I am really not understanding how something as stupidly simple as this raycast does not work. Damn, I am even sure it worked at some point (as the problem in the linked bug report in june was that I had to enforce the complete() and not that the raycast did not hit anything…)
Extracted the code and added some visualization to show that it is not working:
Open SampleScene (there is only one)
Enter Play Mode
Click Button
Check Log
Expected
Every click creates a plane, both as GameObject (to show ordering etc, as well as an Entity with a PhysicsCollider) and as pure Entity. ColliderConsideredBeforeBuildPhysicsWorld marks all RaycastRequest as “seen before” UpdatePhysics and the RaycastSystem which then should before the raycast.
None of the raycasts ever does hit any of the planes, not even those that should now exist for several frames after the first click.
Is there no working tutorial for Raycasts from systems anywhere available? I have seen example for raycasts from Jobs, via RaycastCommand, … but nothing with SystemBase and Entities.ForEach. The Samples project also does not do this - it does run the raycasts of QueryTester in a MonoBehaviour during OnDrawGizmos.
Would have edited the previous post instead of by accident bumping my own thread, but the forum says “Upload unavailable” when I try to attach the zip to the previous psot.
Okay, but then the RaycastSystem must run either before or after both BuildPhysics/ExportPhysicsSystem as it does write the result to a component.
What would be the recommended point to schedule a raycast system before/after?
Now it is getting interesting. I am starting to gather all kinds of ways to do the raycast just to see any implementation working. So far … none.
There are 3 Buttons now:
“SystemBase” - as per this thread (RaycastSystem: SystemBase)
“MainThread” - as per UnityPhysicSamples.QueryTester.RaycastJob with Schedule().Complete()
Also I have added a cube primitive (incl. BoxCollider) with ConvertToEntity (ConvertAndInject) at 0,0,0 with size 1 and Debug.DrawLines to the main thread code. None of the methods does ever hit anything.
The Raycast is always the same : 0,1,0 to 0,-1,0 or vice versa and the result also… nothing ever gets hit.
Edit2: UnityEngine.Physics.Raycast does hit the HitConvertedCube at (0.0, 0.0, 0.0) - as expected.
Edit3: Update attached project to _v3 which includes the UnityEngine.Physics.Raycast.
There are minor issues with scheduling I fixed, but also a major one.
The scheduling stuff: always add UpdateBefore and UpdateAfter, but only if you are in the same group. For example, your ColliderConsideredBeforeBuildPhysicsWorld wants to go before BuildPhysicsWorld, but it is in a SimulationSystemGroup which runs after that, you get a warning about this. Also, for each UpdateBefore, add the GetOutputDependency() of the previous system, and for UpdateAfter AddInputDependency() to the next system. Also, I wouldn’t suggest going after EndFramePhysicsSystem, because then you don’t have “next” in the pipeline. You can also move your entire ray casting system to SimulationSystemGroup, which runs after FixedStepSimulationSystemGroup, so you’ll run after physics for sure. This is it regarding scheduling.
Now the major thing and why ray isn’t hitting - you planned to add a plane collider, but you did it in a strange way, by adding a box collider with scale 0 on the Y axis. That caused the ray to hit an edge case and report no hit because there is no volume (collider starts and ends at same position). You can use the PhysicsShapeAuthoring and set collider type to Plane to achieve the same thing. Or just have a really small Y axis scale if you really want box colliders.
Thanks, I will test this. This could explain the BoxCollider with height 0, but not the PhysicsCollider with a MeshCollider created from the vertex and index information of a quad. The BoxCollider was there to verify the latter, and also display it for debugging purposes.
Edit: For the BoxCollider height 0 you have been absolutely on point. That already helps a lot. I will now investigate the other scenarios.
I’m not sure I’m following. Are you saying that just adding one of the Translation/Rotation/LocalToWorld fixed the issue for you? I know we changed this at some point, but it was a while back.
Exactly, and the query inside the system explicitly states it must have at least one of the given components beside a PhysicsCollider Thank you for your help @petarmHavok !