[0.0.8] Can not access ComponentArray<Class> members from job

Is this intended ?

class Data: MonoBehaviour
{
    NativeArray<float> SomeData;
}

// In a component system somewhere
[BurstCompile]
struct SomeJob: IJob
{
    ComponentArray<Data> stuff:
    public void Execute()
    {
        Data d = stuff[0];
    }
}


//! in the component system update

new SomeJob(){
    stuff = stuffGroup.GetComponentArray<Data>()
}

Produces:

ComponentArray.cs(45,13): error: Unable to get field because it is a class type

Compiler exception: System.InvalidOperationException: Unable to get field because it is a class type
at Burst.Compiler.IL.ILVisitor.Ldfld (Burst.Compiler.IL.Syntax.ILInstruction inst) [0x0001b] in <19eeae212983462598cc0ee7b1756077>:0
at Burst.Compiler.IL.ILVisitor.CompileInternal (Burst.Compiler.IL.Syntax.ILInstruction inst) [0x0043a] in <19eeae212983462598cc0ee7b1756077>:0
at Burst.Compiler.IL.ILVerifier.CompileInternal (Burst.Compiler.IL.Syntax.ILInstruction inst) [0x00000] in <19eeae212983462598cc0ee7b1756077>:0

And a bunch of other subsequent burst error messages.

This, thankfully, works:

class Data: MonoBehaviour
{
    NativeArray<float> SomeData;
}

// In a component system somewhere

[BurstCompile]
struct SomeJob: IJob
{
    NativeArray<float> stuff:
    public void Execute()
    {
        stuff[0] = 1.0f;
    }
}


//! in the component system update

new SomeJob(){
    stuff = stuffGroup.GetComponentArray<Data>()[0].stuff
}

That is intended, you can only access value types in jobs. It’s recommended that you use ComponentDataArray with IComponentData in place of ComponentArray and MonoBehaviours for jobs.

Interesting. The error did not exist prior to 0.0.8. Could also see this pattern being fairly common in Hybrid approaches. I would expect that’s the whole reason for ComponentArray’s existence, in fact.

As for my case, you can not have any real data storage outside of mono land. Having data in MBs is, in fact, the currently recommended way of doing this ( I’d say the only sane way for hybrid ). Doing this while having the nice auto-dependency layer tied to ComponentArray was nice, while it lasted.

Your answer did make me curios as to why I can’t access a pointer ( NativeArray memory ) through a pointer ( a class ). I guess performance reasons ( as it’s hard to predict my access pattern in that component array, or its content’s memory locations ). If so, a warning would of been just as good ?

With Ecs, the recommended data storage is now IComponentData and ComponentDataArrays (Data Oriented Design approach) . If your going the hybrid approach, you can use ComponentDataWrapper to attach an IComponentData to a GameObject just as you would a MonoBehaviour. Bear in mind this is still very much preview software :slight_smile:

Works fine on 0.0.8.

[Serializable]
public struct CitizenData : IComponentData
{
    public int citizenId;
}

public class Citizen : MonoBehaviour
{
    public CitizenData data;
}
public class CitizensManager : ComponentSystem
{
    //Some stuff ...
    public struct SpawnCitizenData
    {
        public int parentBuildingIndex;
        public int citizensCount;
    }

    public struct AllCitizensGroup
    {
        public ComponentArray<Citizen>             Citizen;
        public EntityArray                         Entity;
        public readonly int Length;
    }

    [Inject] public  AllCitizensGroup                  m_AllCitizens;
 
    //Some stuff ...

    struct CalcSpawnCitizensParallelJob : IJobParallelFor
    {
        [ReadOnly] public ComponentArray<Citizen>                  citizens;
        //Some stuff ...

        public void Execute(int index)
        {
         
            if (citizens[i].data.citizenId == 1)
            {
                //Some stuff ...
            }
            //Some stuff ...
        }
    }

    protected override void OnUpdate()
    {
            //Some stuff ...
            var calcCitizensParallelJob = new CalcSpawnCitizensParallelJob
            {
                citizens  = m_AllCitizens.Citizen
            };
            var calcCitizensParallelHandle =
                calcCitizensParallelJob.Schedule(m_Buildings.Length, Mathf.CeilToInt(m_Buildings.Length / 8));
            calcCitizensParallelHandle.Complete();
            //Some stuff ...
            spawnData.Dispose();
            //Some stuff ...
        }
    }
}

Hmm, the only difference seems to be that you are injecting your group as opposed to using GetComponentGroup in my case. Will check this out when I get to my workstation.

If true, it looks like a bug. To be fair, it looked like a bug anyway.