Why isn't this job changing values in the NativeArray?

If it isn’t one ‘gotcha’, it’s another.

If you can’t tell, i’m new to the job system.
I’m working on an astar path finding script with jobs.
I need to generate a grid of nodes to path find through but I’m having trouble setting values for nodes.

I have nodes, and in the job I have a NativeArray of nodes.
I make a couple for loops and set each nodes values to some default values.

The issue I have is all the values return zero after setting them, god knows why. For example, as you’ll see below, ‘isWalkable’ for all nodes are explicitly set to 1 during the loops. Later. If I Debug.Log any node from the array when the job is complete, it prints 0.

So what am I missing now? Why aren’t these values being set?

PathNode Script:

public struct PathNode {
    public int x;
    public int y;
    public int index;
    public int gCost;
    public int hCost;
    public int fCost;
    public int isWalkable;
    public int cameFromNodeIndex;
    public void CalculateFCost() {
        fCost = gCost + hCost;
    }
    public void SetIsWalkable(int isWalkable) {
        this.isWalkable = isWalkable;
    }
}

Pathfinding Script:

using UnityEngine;
using Unity.Mathematics;
using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;
using System.Collections;

public class Pathfinding : MonoBehaviour {
    const int MOVE_STRAIGHT_COST = 10;
    const int MOVE_DIAGONAL_COST = 14;
    public float pathTimeInterval = 1f;
    public int2 startPosition = new int2(0, 0),
                endPosition = new int2(5, 5);
    float intervalTimer;
    public int pathsToTest = 5;
    Coroutine gridCoroutine, pathfindingCoroutine;
    JobHandle generateGridHandle;
    public NativeArray<PathNode> grid;
    public int2 gridSize = new int2(10, 10);
    void Awake() {
        grid = new NativeArray<PathNode>(gridSize.x * gridSize.y, Allocator.Persistent);
    }

    void OnDestroy() {
        grid.Dispose();
    }

    void Update() {
        intervalTimer += Time.deltaTime;
        if (intervalTimer >= pathTimeInterval) {
            intervalTimer = 0;

            if (gridCoroutine != null) {
                StopCoroutine(gridCoroutine);
            }
            gridCoroutine = StartCoroutine(GenerateGrid());
        }
    }

    IEnumerator GenerateGrid() {
        GenerateGridJob gridJob = new GenerateGridJob {
            _gridSize = gridSize,
            pathNodeArray = grid
        };
        generateGridHandle = gridJob.Schedule();
        while (!generateGridHandle.IsCompleted) {

        }
        generateGridHandle.Complete();
        //Why do these return 0?
        Debug.Log(grid[73].isWalkable);
        Debug.Log(gridJob.pathNodeArray[73].isWalkable);
        yield return null;
    }

    [BurstCompile]
    struct GenerateGridJob : IJob {
        public int2 _gridSize;
        public NativeArray<PathNode> pathNodeArray;

        public void Execute() {
            pathNodeArray = new NativeArray<PathNode>(_gridSize.x * _gridSize.y, Allocator.Temp);
            Debug.Log("Generating Grid...");
            for (int x = 0; x < _gridSize.x; x++) {
                for (int y = 0; y < _gridSize.y; y++) {
                    PathNode pathNode = new PathNode();
                    pathNode.x = x;
                    pathNode.y = y;
                    pathNode.index = CalculateIndex(x, y, _gridSize.x);
                    pathNode.gCost = int.MaxValue;
                    //pathNode.hCost = CalculateDistanceCost(new int2(x, y), _endPosition);
                    pathNode.CalculateFCost();
                    pathNode.isWalkable = 1;
                    pathNode.cameFromNodeIndex = -1;
                    pathNodeArray[pathNode.index] = pathNode;
                }
            }
        }

        int CalculateIndex(int x, int y, int gridWidth) {
            return x + y * gridWidth;
        }
        int CalculateDistanceCost(int2 aPosition, int2 bPosition) {
            int xDistance = math.abs(aPosition.x - bPosition.x);
            int yDistance = math.abs(aPosition.y - bPosition.y);
            int remaining = math.abs(xDistance - yDistance);
            return MOVE_DIAGONAL_COST * math.min(xDistance, yDistance) + MOVE_STRAIGHT_COST * remaining;
        }
    }
}

On row 59 you immediately override pathNodeArray struct, and your grid not using anymore as expected, and as expected it’s values never changes.

1 Like

Line 41-43: You did not set _startPosition, _endPosition and _gridSize of GenerateGridJob and may be default value 0. So its loop do nothing.

Whoops. I forgot to remove these variables before posting. _startPosition and _endPosition shouldnt be there. I did forget to set grid size too.

However, my problem still persists after these changes. I edited my post to reflect these changes though.

As @eizenhorn said, you must not create new instance of pathNodeArray in the job. Remove line 62.

1 Like

TY! This was it. Now I understand the weirdness I was experiencing.

It helps to be consistent with naming. If the caller has a collection named “grid” and it gets assigned to a job whose field calls it “pathNodeArray” it’s only a matter of time where you get bitten by the fact that, even though they are named quite differently, they’re actually the same collection.

I “Refactor => Rename” all the time. Like hundreds of times a day! :wink:

1 Like

Fair.