Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Unity Jobsystem read from MonoBehavior GameObject

Discussion in 'C# Job System' started by VisDev96, Jun 19, 2023.

  1. VisDev96

    VisDev96

    Joined:
    Jul 18, 2021
    Posts:
    3
    Hello,

    it's my first time posting and i am not sure if this is the right place but the forum is kinda confusing.

    in my game my pathifnding class uses the unity Job-Interface. In the Execute i am making the following call :


    var indexOfTile = TileMap.instance.walledNeighbors.FindIndex(n => n.Item1 == currentTile.tileCoords);

    to get some information about a Vector3Int i am currently processing.

    is this read from a gameobject in the job ok? i am not getting any errors and the this data where i am reading from is only changed once when the map loads.

    i am also creating a List inside my execute that is only used within to do some easy filtering with data etc.
    i know your supposed to only use NativeArrays and such and i am generally using them.

    Thanks for any help.
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    Please post more code. Specifically, we don't know what Type walledNeighbors is and what kind of Job (presumably not parallel?) we're talking about and whether it has the [BurstCompile] attribute and so on.

    Generally speaking I'm surprised that this is working without errors because I smell several issues here. It does look a lot like code where you really haven't adapted to how Jobs work and maybe it works but you're not using the potential for bursted jobs at all. Any job that "calls out" into managed space like this just in order to get some data is a design smell. The job should have direct access to the data to begin with.
     
  3. VisDev96

    VisDev96

    Joined:
    Jul 18, 2021
    Posts:
    3



    This is my Pathinfding class with its execute

    Code (csharp):
    1.  
    2.  
    3.  public PathfindingJob(Vector3Int start, Vector3Int dest, TileData[,,] matrix, int maxPathLen)
    4.     {
    5.         this._status = new NativeArray<Pathfinding.Status>(1, Allocator.Persistent);
    6.         this._status[0] = Pathfinding.Status.UNKNOWN;
    7.         this.pathLen = new NativeArray<int>(1, Allocator.Persistent);
    8.         this.pathLen[0] = 0;
    9.         this.maxPathLen = maxPathLen;
    10.         this.size = TileMapUtils.getSize(matrix);
    11.         this.sizeCoeffs = TileMapUtils.getSizeCoeffs(this.size);
    12.         this.start = TileData.from(matrix, start);
    13.         this.dest = TileData.from(matrix, dest);
    14.         this._path = new NativeArray<TileData>(maxPathLen, Allocator.Persistent);
    15.         var arrays = TileMapUtils.tileArrayFrom(matrix, this.size);
    16.         this.tileData = arrays.tileData;
    17.         this.walledNeighbors = arrays.walledNeighbors;
    18.     }
    19.  
    20.     public void Execute()
    21.     {
    22.         if (!this.dest.accessible)
    23.         {
    24.             // no path exists from start to dest
    25.             _status[0] = Pathfinding.Status.NO_PATH;
    26.             return;
    27.         }
    28.  
    29.         var dest = this.dest;   // won't compile without this line :)
    30.         IComparer<PathTile> tileComparer = Comparer<PathTile>.Create((t1, t2) =>
    31.         {
    32.             // TODO: Shouldn't this heuristic also consider costFromStart?
    33.             var diff = t1.self.relativeDistanceFrom(dest) - t2.self.relativeDistanceFrom(dest);
    34.             int clampedDiff = (int)diff;
    35.             if (diff > 0f && diff <= 0.5f) clampedDiff = 1;
    36.             if (diff < 0f && diff >= -0.5f) clampedDiff = -1;
    37.             return clampedDiff;
    38.         });
    39.  
    40.         IComparer<TileData> tileComparer_ = Comparer<TileData>.Create((t1, t2) =>
    41.         {
    42.             // TODO: Is this duplicate comparer even needed (see below)?
    43.             var diff = t1.relativeDistanceFrom(dest) - t2.relativeDistanceFrom(dest);
    44.             int clampedDiff = (int)diff;
    45.             if (diff > 0f && diff <= 0.5f) clampedDiff = 1;
    46.             if (diff < 0f && diff >= -0.5f) clampedDiff = -1;
    47.             return clampedDiff;
    48.         });
    49.  
    50.         var bestPredecessors = new Dictionary<TileData, TileData>();
    51.         var closedList = new HashSet<TileData>();
    52.         var cumulativeOpenList = new Dictionary<TileData, PathTile>();
    53.         var openList = new List<PathTile>();
    54.         PathTile startTileData = new PathTile(start, start, 0, start.relativeDistanceFrom(dest));
    55.         openList.Add(startTileData);
    56.         // TODO: Should we add the start tile to the cumulativeOpenList?
    57.         while (openList.Count > 0)
    58.         {
    59.             // get first open node
    60.             PathTile current = openList[0];
    61.             openList.RemoveAt(0);
    62.             TileData currentTile = current.self;
    63.             PathTile currentTileData = current;
    64.             // stop if we have reached the destination
    65.             if (currentTile == dest)
    66.             {
    67.                 var foundPath = new List<TileData>();
    68.                 while (currentTile != start)
    69.                 {
    70.                     foundPath.Insert(0, currentTile);
    71.                     currentTile = cumulativeOpenList[currentTile].bestPredecessor;
    72.                 }
    73.                 foundPath.Insert(0, start);
    74.                 _status[0] = Pathfinding.Status.PATH_OK;
    75.  
    76.                 // check that we won't exceed maxPathLen
    77.                 pathLen[0] = foundPath.Count;
    78.                 if (pathLen[0] > maxPathLen)
    79.                 {
    80.                     pathLen[0] = maxPathLen;
    81.                     _status[0] = Pathfinding.Status.MAX_PATH_LEN_EXCEEDED;
    82.                 }
    83.  
    84.                 for (int i = 0; i < pathLen[0]; i++) this._path[i] = foundPath[i];
    85.                 return;
    86.             }
    87.             //
    88.             var indexOfTile = TileMap.instance.walledNeighbors.FindIndex(n => n.Item1 == currentTile.tileCoords);
    89.             // add neighbors to open list sorted by distance
    90.             var neighbors = new List<TileData>();
    91.    
    92.             neighbors = TileMapUtils.neighborsOf(currentTile, size, sizeCoeffs, tileData)
    93.                 .Where(n
    94.                     => n != null
    95.                     && !n.occupied
    96.                     && n.accessible
    97.                     && !closedList.Contains(n))
    98.                 .OrderBy(_ => _, tileComparer_)   // TODO: Since we're sorting the openList afterwards anyway, do we even need to sort here?
    99.                 .ToList();
    100.             var counter = 1;
    101.             foreach (var neighbor in neighbors)
    102.             {
    103.                 PathTile neighborData = new PathTile(neighbor, currentTile, currentTileData.costFromStart + neighbor.movementCost, neighbor.relativeDistanceFrom(dest));
    104.                 counter++;
    105.                 var prevSelf = openList.FindIndex(n => n.self.tileCoords == neighbor.tileCoords);
    106.                 if (prevSelf != -1)
    107.                 {
    108.                     if (openList[prevSelf].costFromStart + openList[prevSelf].costToDest > neighborData.costFromStart + neighborData.costToDest)
    109.                     {
    110.                         openList[prevSelf] = neighborData;
    111.                         openList.Sort(tileComparer);    // TODO: Shouldn't we sort once after the loop, regardless of conditions?
    112.                         cumulativeOpenList[neighbor] = neighborData;
    113.                     }
    114.                 }
    115.                 else
    116.                 {
    117.                     openList.Add(neighborData);
    118.                     cumulativeOpenList[neighbor] = neighborData;
    119.                 }
    120.             }
    121.             closedList.Add(currentTile);
    122.         }
    123.  
    124.         // no path exists from start to dest
    125.         _status[0] = Pathfinding.Status.NO_PATH;
    126.     }
    127.  
    128. }
    129.  
    130.  
    walledNeighbors has the following type:
    Code (csharp):
    1.  
    2.     public List<(Vector3Int, List<Vector3Int>)> walledNeighbors;
    3.  
    there is some dead code in the excute because im currently testing stuff
     
    Last edited: Jun 19, 2023
  4. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    So basically from the looks of it you wrote a "managed" job. Put the [BurstCompile] attribute just above the struct declaration and you'll see the errors you aren't getting. ;)

    Note that if the struct isn't actually implementing the IJob interface or one of its variants, it's not a job. It's just a struct with methods and you can do anything with it. I'd say what you have is just a single-threaded managed method called "Execute", it certainly wouldn't work if it were a nonmanaged, bursted job.

    A couple things that won't work in an actual job's Execute method (from the top of my head so it may not be perfectly accurate, and there's probably a lot more since I just glanced over the code):
    • any managed object such as classes, interfaces, arrays[]
    • LINQ methods (Where, OrderBy, and so on)
    • lambda expressions / anonymous delegates
    • managed collections: Dictionary, HashSet, List
    • accessing statics is doable but caveats apply
    Given the current looks of the code it'll give you more than just a headache for a day trying to rewrite this to an actual bursted job. But it's going to be worth it! ;)
     
  5. VisDev96

    VisDev96

    Joined:
    Jul 18, 2021
    Posts:
    3

    alright thanks for the answers/help. Seems i did not quite understand how that works. Changing this code will be quite an investment