Search Unity

Question Want to understand unsafe keyword and Raycast jobs

Discussion in 'Entity Component System' started by FederalRazer89, Nov 17, 2021.

  1. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Hello

    I have spend a bit too long time trying to read documentations, tutorials and videos trying to find how I can my raycasts into jobs. But most examples I found with raycast jobs is done with the unsafe, so I am a bit unsure of how to approach this. According to documentation on unsafe keyword, it will enable pointers and references like in C++.
    Do I need to understand C++ pointers and references to be able to use raycast jobs? Just need to know so i know were i should start reading.

    C# documentation
    https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/unsafe-code
    https://docs.unity3d.com/Packages/com.unity.physics@0.0/manual/collision_queries.html


    Working implementation of raycasting on controller
    Code (CSharp):
    1.     public class CharacterControllerSystem : SystemBase
    2.     {
    3.  
    4.         private BuildPhysicsWorld buildPhysicsWorldSystem;
    5.        
    6.         protected override void OnCreate()
    7.         {
    8.  
    9.             buildPhysicsWorldSystem = World.GetExistingSystem<BuildPhysicsWorld>();
    10.         }
    11.        
    12.  
    13.         protected override void OnUpdate()
    14.         {
    15.             var rayCaster = new MovementRayCast() { physicsWorld = World.GetExistingSystem<BuildPhysicsWorld>().PhysicsWorld };
    16.  
    17.             float deltaTime = Time.DeltaTime;
    18.  
    19.  
    20.             Entities.ForEach((ref CharacterDataComponent characterDataComponent, ref Translation translation, ref Rotation rotation, in PlayerControllerComponent playerControllerComponent) =>
    21.             {
    22.                
    23.                 float3 upwardCorrection = (-characterDataComponent.gravity) * 0.3f;
    24.  
    25.                 //Rotation camera independent
    26.                 if (playerControllerComponent.leftAlt)
    27.                 {
    28.  
    29.                 }
    30.                 else
    31.                 {
    32.                     rotation.Value = quaternion.RotateY(playerControllerComponent.cameraHorizontal);
    33.                 }
    34.  
    35.                 //Driving the gameobject with data from PlayerControllerComponent
    36.                 float3 vectorRight = math.mul(rotation.Value, math.right());
    37.                 vectorRight.y = 0;
    38.                 vectorRight = math.normalizesafe(vectorRight);
    39.  
    40.                 translation.Value = translation.Value + (playerControllerComponent.inputVertical * math.forward(rotation.Value)) * 5f * deltaTime;
    41.                 translation.Value = translation.Value + (playerControllerComponent.inputHorizontal * vectorRight) * 5f * deltaTime;
    42.  
    43.                 characterDataComponent.centerPosition = new float3(translation.Value.x, translation.Value.y + characterDataComponent.centerHeight, translation.Value.z);
    44.  
    45.                
    46.                 //jumping TODO add acceleration to jump
    47.                 if (playerControllerComponent.jump && characterDataComponent.IsGrounded)
    48.                 {
    49.                     translation.Value = translation.Value + (-characterDataComponent.gravity) * 60f * deltaTime;
    50.                 }
    51.  
    52.  
    53.                 //Raycasting end position
    54.                 var rayCastEnd90Pos = new float3(0, -characterDataComponent.centerHeight, 0);
    55.                 var rayCastEndSlopePos = math.mul(rotation.Value, rayCastEnd90Pos);
    56.  
    57.                 //check for ground. TODO Add somekind of range check to halt the upward movement, mabye use lerp
    58.                 for (int i = 0; i < 10; i++)
    59.                 {
    60.                     float angle = 36 * i;
    61.  
    62.                     float3 checkPositionRotation = math.mul(quaternion.RotateY(angle), new float3(0,0,0.25f));
    63.                     float3 checkPosition = characterDataComponent.centerPosition + checkPositionRotation;
    64.  
    65.                     float3 checkPositionEnd = checkPosition + new float3(0, -characterDataComponent.centerHeight, 0);
    66.                     characterDataComponent.debugPosition = checkPositionEnd;
    67.                     Unity.Physics.RaycastHit raycastHit = new Unity.Physics.RaycastHit();
    68.  
    69.  
    70.                     //Debug.DrawLine(checkPosition, checkPositionEnd, Color.green,1f);
    71.  
    72.  
    73.                     if (rayCaster.CheckRay(checkPosition, checkPositionEnd, out raycastHit))
    74.                     {
    75.                         float distance = math.distance(checkPositionEnd, raycastHit.Position);
    76.  
    77.                         if (distance> 0.1f)
    78.                         {
    79.                             translation.Value = translation.Value + upwardCorrection * deltaTime;
    80.                         }
    81.  
    82.                         characterDataComponent.IsGrounded = true;
    83.                         break;
    84.                     }
    85.                     else
    86.                     {
    87.                         characterDataComponent.IsGrounded = false;
    88.                     }
    89.                    
    90.                 }
    91.  
    92.  
    93.                 if (!characterDataComponent.IsGrounded)
    94.                 {
    95.  
    96.                     translation.Value = translation.Value + characterDataComponent.gravity * deltaTime;
    97.  
    98.                 }
    99.                
    100.                 //check for Step or slope
    101.  
    102.             }).Schedule();
    103.  
    104.             buildPhysicsWorldSystem.AddInputDependencyToComplete(Dependency);
    105.  
    106.            
    107.         }
    108.  
    109.      
    110.         private struct MovementRayCast
    111.         {
    112.             public PhysicsWorld physicsWorld;
    113.  
    114.             public bool CheckRay(float3 startingPosition, float3 endPosition, out Unity.Physics.RaycastHit raycastHit)
    115.             {
    116.                 var ray = new RaycastInput()
    117.                 {
    118.                     Start = startingPosition,
    119.                     End = endPosition,
    120.                     Filter = new CollisionFilter()
    121.                     {
    122.                         GroupIndex = 0,
    123.                         BelongsTo = 1u << 2,
    124.                         CollidesWith = 1u << 1
    125.                     }
    126.                 };
    127.  
    128.                 return physicsWorld.CastRay(ray,out raycastHit);
    129.             }
    130.  
    131.  
    132.         }
     
  2. Resshin27

    Resshin27

    Joined:
    Apr 21, 2018
    Posts:
    31
    Simply put, you do not need to understand C++ pointers and references to be able to use raycast jobs.

    Pointers and refs work subtly different in C#, so its better you stick with the C# way first.
    For C# pointers and unsafe code, C# documentation works as a good starting point, and you have already found that.

    Not related to raycasting but, if you want to delve deeper in how unsafe code and pointers are being used in C# world by Unity, check source code of Unity.Collections package. You can start by checking implementation of UnsafeList and NativeList.

    In UnityCsReference github, you can check NativeArray.cs for more unsafe and pointer code written in C#.
     
    FederalRazer89 likes this.
  3. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Thanks for clarifying. Saved me a detour, was about to dive in to pointers, before i got a raycast job working.


    Didn't know that i could find that there.
     
  4. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    I have done some test which didn't work and think i understand more but still not making progress.

    When i am trying to do a simple test i get a error about collisionWorld being a reference type. So i think i am doing something wrong and the Docs don't show the entire solution so i don't know what more syntax i need to use RaycastJob and ScheduleBatchRayCast.

    What am i missing from the example at the documentation?

    Test Project\Assets\Scrips\Test\ECSRaycastTest\ECSRaycastTesting.cs(26,9): error Assets\Scrips\Test\ECSRaycastTest\ECSRaycastTesting.cs(26,9): error DC0001: Entities.ForEach Lambda expression uses field 'collisionWorld in a reference type'. Either assign the field to a local outside of the lambda expression and use that instead, or use .WithoutBurst() and .Run()

    Code:
    Code (CSharp):
    1.  
    2.     private BuildPhysicsWorld buildPhysicsWorldSystem;
    3.     private CollisionWorld collisionWorld;
    4.  
    5.     protected override void OnCreate()
    6.     {
    7.  
    8.         buildPhysicsWorldSystem = World.GetExistingSystem<BuildPhysicsWorld>();
    9.         collisionWorld = buildPhysicsWorldSystem.PhysicsWorld.CollisionWorld;
    10.     }
    11.  
    12.     protected override void OnUpdate()
    13.     {
    14.        
    15.         Entities.ForEach((ref ECSRaycastTestingData ecsRaycastTestingData, in Translation translation) =>
    16.         {
    17.             NativeList<RaycastInput> raycastInputs = new NativeList<RaycastInput>(Allocator.Temp);
    18.             NativeList<RaycastHit> raycastHitList = new NativeList<RaycastHit>(Allocator.Temp);
    19.  
    20.             for (int i = 0; i < 10; i++)
    21.             {
    22.                 float angle = 36 * i;
    23.                 float3 startPos = math.mul(quaternion.RotateY(angle), new float3(0, 0, 0.25f));
    24.  
    25.                 RaycastInput ray = new RaycastInput()
    26.                 {
    27.                     Start = startPos + translation.Value,
    28.                     End = startPos + translation.Value + new float3(0, -5, 0),
    29.                     Filter = CollisionFilter.Default
    30.  
    31.                 };
    32.                 raycastInputs.Add(ray);
    33.  
    34.  
    35.             }
    36.             var raycastCollection = ScheduleBatchRayCast(collisionWorld, raycastInputs, raycastHitList);
    37.  
    38.             raycastCollection.Complete();
    39.  
    40.  
    41.         }).Schedule();
    42.  
    43.     }
    Code from docs
    Code (CSharp):
    1.    
    2. [BurstCompile]  public struct RaycastJob : IJobParallelFor
    3.     {
    4.         [ReadOnly] public CollisionWorld collisionWorld;
    5.         [ReadOnly] public NativeArray<RaycastInput> raycastInput;
    6.         public NativeArray<RaycastHit> raycastHit;
    7.  
    8.         public void Execute(int index)
    9.         {
    10.             RaycastHit hit;
    11.             collisionWorld.CastRay(raycastInput[index], out hit);
    12.             raycastHit[index] = hit;
    13.         }
    14.     }
    15.  
    16.  
    17.     public static JobHandle ScheduleBatchRayCast(CollisionWorld collisionWorld, NativeArray<RaycastInput> raycastInput, NativeArray<RaycastHit> raycastHit)
    18.     {
    19.         JobHandle rcj = new RaycastJob
    20.         {
    21.             raycastInput = raycastInput,
    22.             raycastHit = raycastHit,
    23.             collisionWorld = collisionWorld
    24.  
    25.         }.Schedule(raycastInput.Length, 5);
    26.         return rcj;
    27.     }