Search Unity

How to ensure Physics World is updated within the same frame?

Discussion in 'Physics for ECS' started by MNNoxMortem, Oct 27, 2019.

  1. MNNoxMortem

    MNNoxMortem

    Joined:
    Sep 11, 2016
    Posts:
    723
    I have created simple MeshCollider from a quad of size 200x200 at origin and if I try to raycast against it immediatly afterwards I am unable to hit it - likely because the PhysicsSystem did not run yet. How do I force an Update of the PhysicsWorld to immediatly be able to hit it.

    It looks like I never hit a collider until I explicitly call
    physicsWorldSystem.Update(); (edit: and wait until the next frame) but in the examples I could not find a similar call, therefore I assume this should not be required.
    Code (CSharp):
    1. var debugPlaneMesh = new Mesh {
    2.     indexFormat = IndexFormat.UInt16,
    3.     vertices = new[] {
    4.         new Vector3(-100, 0, -100), // 0
    5.         new Vector3(100,  0, -100), // 1
    6.         new Vector3(100,  0, 100),  // 2
    7.         new Vector3(-100, 0, 100),  // 3
    8.     },
    9.     triangles = new[] {0, 1, 2, 2, 3, 0}
    10. };
    11. var goDebugPlaneMesh         = new GameObject();
    12. var goDebugPlaneMeshRenderer = goDebugPlaneMesh.AddComponent<MeshRenderer>();
    13. var goDebugPlaneMeshFilter   = goDebugPlaneMesh.AddComponent<MeshFilter>();
    14. goDebugPlaneMeshFilter.mesh = debugPlaneMesh;
    15.  
    16. var debugPlaneEntity = entityManager.CreateEntity();
    17. entityManager.AddComponentData(debugPlaneEntity, new LocalToWorld());
    18. entityManager.AddComponentData(debugPlaneEntity, new Translation());
    19. entityManager.AddComponentData(debugPlaneEntity, new Rotation());
    20. BlobAssetReference<Collider> debugPlaneCollider =
    21.     Unity.Physics.MeshCollider.Create(debugPlaneMesh.vertices.Select(v => new float3(v.x, v.y, v.z)).ToArray(),
    22.                                       debugPlaneMesh.triangles);
    23. var debugPlanePhysicsCollider = new PhysicsCollider {
    24.     Value = debugPlaneCollider
    25. };
    26. entityManager.AddComponentData(debugPlaneEntity, debugPlanePhysicsCollider);
    27.  
    28. this.LogInfo($"debugEntity is e.Index:{debugPlaneEntity.Index},e.Version:{debugPlaneEntity.Version}");
    29. var physicsWorldSystem =
    30.     Unity.Entities.World.Active.GetExistingSystem<Unity.Physics.Systems.BuildPhysicsWorld>();
    31. ref PhysicsWorld physicsWorld = ref physicsWorldSystem.PhysicsWorld;
    32.  
    33. Unity.Physics.RaycastInput input = new Unity.Physics.RaycastInput() {
    34.     Start  = new float3(0, 100,  0),
    35.     End    = new float3(0, -100, 0),
    36.     Filter = CollisionFilter.Default
    37. };
    38.  
    39. Unity.Physics.RaycastHit hit = new Unity.Physics.RaycastHit();
    40. bool haveHit = physicsWorld.CastRay(input, ou-t hit);
    41. if (haveHit)
    42. {
    43.     Entity e = physicsWorldSystem.PhysicsWorld.Bodies[hit.RigidBodyIndex].Entity;
    44.     Debug.Log(($"e.Index:{e.Index},e.Version:{e.Version}");
    45.     Debug.Log(($"hit.Position:{hit.Position},hit.SurfaceNormal:{hit.SurfaceNormal},hit.ColliderKey{hit.ColliderKey}");
    46. }
    47. else
    48. {
    49.     Debug.Log(("Hit nothing"); // will not hit if casted immediatly after creating it
    50. }
     
    Last edited: Oct 27, 2019
  2. MNNoxMortem

    MNNoxMortem

    Joined:
    Sep 11, 2016
    Posts:
    723
    After experimenting a bit with it, it looks like I never hit any collider until I manually call physicsWorldSystem.Update(). This is not how I would expect it to work - I would have expected the Physics System to update each frame by Unity. What am I doing wrong?

    I have downloaded the physics example and do not see an explicit call to Update anywhere.

    Edit: This is driving me crazy. Could it be that the result of PhysicsWorldSystem.Update() will not be in effect until the next frame? If I run the the code twice (click twice), the second time the same raycast will hit the collider, but the first time it will miss it.
     
    Last edited: Oct 27, 2019
  3. MNNoxMortem

    MNNoxMortem

    Joined:
    Sep 11, 2016
    Posts:
    723
    What could be a potential reason the BuildPhysicsWorld is inactive although it shows that one group contains the Entity I want to have updated?
    upload_2019-11-2_22-41-47.png

    I have found this out after trying to figure out that why my collider cannot be hit via a Raycast at all unless I force update it?
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    You shouldn't need to be updating these systems yourself. What you can do is add a dependency to the physics system that creates the world to ensure it's updated before you do your raycast

    Code (CSharp):
    1.  
    2. /// <inheritdoc/>
    3. protected override void OnCreate()
    4. {
    5.    this.physicsWorldSystem = this.World.GetOrCreateSystem<BuildPhysicsWorld>();
    6. }
    7.  
    8. /// <inheritdoc/>
    9. protected override JobHandle OnUpdate(JobHandle handle)
    10. {
    11.    handle = JobHandle.CombineDependencies(handle, this.physicsWorldSystem.FinalJobHandle);
    12.  
    13.    // create your job etc that needs to use the physics world
    14.    handle = new ObstructionChangedJob
    15.    {
    16.        CollisionWorld = this.physicsWorldSystem.PhysicsWorld.CollisionWorld,
    17.    }.Schedule(this, handle);
    18.  
    19.    return handle;
    20. }
    21.  
     
    Last edited: Nov 3, 2019
    steveeHavok and MNNoxMortem like this.
  5. MNNoxMortem

    MNNoxMortem

    Joined:
    Sep 11, 2016
    Posts:
    723
    @tertle Thank you for this and all the other answers you post here on the forum. Helpful as always!
     
  6. MNNoxMortem

    MNNoxMortem

    Joined:
    Sep 11, 2016
    Posts:
    723
    Thinking about it, I think I will have to use manual Update for the following scenario:
    • Raycast through a scene against placeholder colliders (level of detail 0)
    • Load actual colliders
    • Raycast through the scene against actual colliders
    • Repeat with hitpoints as new origin with a new direction (until maximum recursion level X is reached)
    In this case I believe I will require to update after every group of load operations manually, s.t. I am able to calculate the next batch of raycasts within the same frame.

    Assuming I have a small (e.g. 4) level of recursions, would it be possible to create a chain which incorporates the BuildPhysicsWorld System multiple times via job dependencies?
    1. BuildPhysicsWorld (1st time)
    2. Raycast 1, Load new Colliders
    3. BuildPhysicsWorld (2nd time)
    4. Raycast 2, Load new Colliders
    5. BuildPhysicsWorld (3rd time)
    6. Raycast 3, Load new Colliders
    7. BuildPhysicsWorld (4rd time)
    8. Raycast 4 (final)

    So far all examples I have seen expect a linear order of systems (or to be precise a directional acyclic graph of dependencies with each system as a single node compared to this chain which has 4 nodes of the BuildPhysicsWorld at 1., 3., 5. and 7.).
     
  7. MNNoxMortem

    MNNoxMortem

    Joined:
    Sep 11, 2016
    Posts:
    723
    @tertle - scheduling the job handle against physicsWorld.FinalJobHandle worked!
     
    tertle likes this.