Problems with passing NativeArrays

Hi, I’m somewhat new to Unity ECS and I’m having issues trying to pass NativeArrays. First, I tried using direct access to a static class inside a job and it worked fine, but I know its not a good practice so I tried to switch to NativeArray in the code below. It’s not working as intended though.

In the Code Part 1, my job called KnockbackJob receives the NativeArray collisionArray that is initialized using the function GetCollisionNativeArray() from the static class, so its not accessing directly the static class from inside of a job. But for some reason, it just give me random behavior, but when I access the static Collision[,] from the static class inside a job it gives me the expected behavior. I don’t know if its something simple or complex but I’m really confused about trying to pass this NativeArray that should behaves like the Collision[,] but it doesn’t.

If any questions popped up because of my attempt to explain my code, just ask, I would be glad to give any other information that I can.

Code Part 1

    private struct KnockbackJob : IJobProcessComponentData<Movement, Components.Collider2D>
    {
        [ReadOnly]
        public float time;
        [ReadOnly]
        public float knockbackDuration;
        [ReadOnly]
        public int arrayLength;
        [ReadOnly]
        [DeallocateOnJobCompletion]
        public NativeArray<Collision> collisionArray;

        public void Execute(ref Movement movement, ref Components.Collider2D collider2D)
        {
            //collider2D.ID = receiverID
            //CollisionMatrix.collisionMatrix[collider2D.Index][i].ID = senderID
            Collision collision;
            for (int i = 0; i < arrayLength; i++)
            {
                //Not Working
                collision = collisionArray[collider2D.Index * arrayLength + i];
                //Working
                collision = CollisionMatrix.collisionMatrix[collider2D.Index, i];

               //Do Stuff based on collision
            }
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        NativeArray<Collision> collisionArray = CollisionMatrix.GetCollisionNativeArray();
        int length = CollisionMatrix.arrayLength;
        var knockbackJob = new KnockbackJob
        {
            time = Time.time,
            knockbackDuration = 1f,
            arrayLength = length,
            collisionArray = collisionArray,
        };
        JobHandle knockbackJobHandle = knockbackJob.Schedule(this, inputDeps);
        return knockbackJobHandle;
     }

Code Part 2 Code Part 2 Code Part 2 Code Part 2

public static class CollisionMatrix
{
    public static int arrayLength = 0;
    public static Collision[,] collisionMatrix = new Collision[0,0];//[receiverIndex, senderIndex]

    public static NativeArray<Collision> GetCollisionNativeArray()
    {
        NativeArray<Collision> collisionArray = new NativeArray<Collision>(arrayLength * arrayLength, Allocator.TempJob);
        for(int i = 0; i < arrayLength; i++)
        {
            for(int j = 0; j < arrayLength; j++)
            {
                collisionArray[i * arrayLength + j] = collisionMatrix[i,j];
            }
        }
        return collisionArray;
    }

    public static void SetupCollisionMatrix(int length)
    {
        arrayLength = length;
        collisionMatrix = new Collision[length, length];
    }
    public static void LoadCollisionMatrixLine(int i, NativeArray<Collision> collisionArray, int length)
    {
        for (int j = 0; j < length; j++)
        {
            collisionMatrix[i, j] = collisionArray[i * length + j];
        }
    }
}


public struct Collision
{
    private int IsColliding;
    private int IsCollidingEnter;
    private int IsCollidingExit;
   
    public float Data;
    public Element Element;
    public CollisionAction CollisionAction;
       
    public int GetCollision()
    {
        return IsColliding;
    }
    public int GetCollisionEnter()
    {
        return IsCollidingEnter;
    }
    public int GetCollisionExit()
    {
        return IsCollidingExit;
    }
    public void SetCollision(bool isColliding)
    {
        if (isColliding)
        {
            if(IsColliding == 1)
            {
                IsCollidingEnter = 0;
            } else
            {
                IsCollidingEnter = 1;
            }
            IsColliding = 1;
            IsCollidingExit = 0;
        } else
        {
            if(IsColliding == 0)
            {
                IsCollidingExit = 0;
            } else
            {
                IsCollidingExit = 1;
            }
            IsColliding = 0;
            IsCollidingEnter = 0;
        }
    }
    public void SetData(float data = 0, Element element = Element.Physical)
    {
        Data = data;
        Element = element;
    }
    public void SetIDs(ID receiverID, ID senderID)
    {
        CollisionAction = CollisionInterpreter.GetAction(receiverID, senderID);
    }
    public Collision(ID receiverID, ID senderID, float data = 0, Element element = Element.Physical)
    {
        IsColliding = 0;
        IsCollidingEnter = 0;
        IsCollidingExit = 0;
        Data = data;
        Element = element;
        CollisionAction = CollisionInterpreter.GetAction(receiverID,senderID);
    }
}

In your case i think it makes more sense to use NativeMultiHashMap instead of NativeArray.
But remember that when you are getting your NativeArray your getting a copy of the values not a ref. That said if you change values on “collisionMatrix” they do not reflect on that NativeArray.

For more about NativeMultiHashMap read this

That’s not true.

NativeArray just holds a pointer to a chunk of memory. When a copy is made you get a copy of the pointer but it still points to the exact same location in memory. Any changes will be reflected in the original array.

2 Likes

In my case, I’m just using it as a ReadOnly array and I’m not writing or modifying any Collision.
And, its just that the way of getting those values is:
Collision[,] => CheckInsideJob
or
Collision[,] => NativeArray => Pass to Job => CheckInsideJob
is giving me totally different results.

1 Like

I’m mean the way he is getting the NativeArray here:

    public static NativeArray<Collision> GetCollisionNativeArray()
    {
        NativeArray<Collision> collisionArray = new NativeArray<Collision>(arrayLength * arrayLength, Allocator.TempJob);
        for(int i = 0; i < arrayLength; i++)
        {
            for(int j = 0; j < arrayLength; j++)
            {
                collisionArray[i * arrayLength + j] = collisionMatrix[i,j];
            }
        }
        return collisionArray;
    }

He is clearly getting a copy of the values from collisionMatrix? Or am i missing something here?
Not that it matters in the currently described scenario.

I still think the solution for that is NativeMultiHashMap

Oh yeah sure, sorry misunderstood you.

1 Like

You are pretty right, I’m getting a copy from the collisionMatrix, but I just want to read from those values, not modify any of those. Should I change to NativeMultiHashMap for that purpose?

I found my error, I was creating race conditions because of another system. The nativeArray was being initialized before the other job had finish to set the matrix for that frame. Thanks for helping.