Reading Component Data from within Job

I’m trying to create a follow-leader system so that followers of a leader will move their position to the leader’s position. I’ve seen that you can use ComponentDataFromEntity to read component data while inside a job, but because I’m reading the leader’s Translation components and I want to write to the follower’s Translation components, it throws an exception. Even if I use [NativeDisableParallelForRestriction] it still doesn’t bypass the race condition detection. Is this possible within a job?

One idea I had was to write another component system that just writes to the follower’s translation component at the end of the frame and write to the new component in my follow-leader system, but that seems like bad ECS design.

Another idea was to get a list of Translation components in one job, and then pass that in a native array to a second job which reads from it to write to the translation component of the follow entity. Is there a better way to do this?

Code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Collections;
using Unity.Burst;
public class FollowLeaderSystem : JobComponentSystem
{

    struct FollowLeaderJob : IJobChunk
    {
        public ArchetypeChunkComponentType<Translation> TranslationType;
        [ReadOnly] public ArchetypeChunkComponentType<FollowLeader> FollowLeaderType;
        [NativeDisableParallelForRestriction] [ReadOnly] public ComponentDataFromEntity<Translation> LeaderTranslation;

        public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
        {
            NativeArray<Translation> Translations = chunk.GetNativeArray(TranslationType);
            NativeArray<FollowLeader> FollowLeaders = chunk.GetNativeArray(FollowLeaderType);
           
            for(int i = 0; i < chunk.Count; i++)
            {
                Translation translation = Translations[i];
                FollowLeader followLeader = FollowLeaders[i];

                if (!LeaderTranslation.Exists(followLeader.Leader))
                    return;
               
                Translation leaderTranslation = LeaderTranslation[followLeader.Leader];
                translation.Value = leaderTranslation.Value;

                Translations[i] = translation;

            }

        }
    }

    EntityQuery m_Query;

    protected override void OnCreate()
    {
        m_Query = GetEntityQuery(typeof(Translation), ComponentType.ReadOnly<FollowLeader>());
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {

        var job = new FollowLeaderJob()
        {
            TranslationType = GetArchetypeChunkComponentType<Translation>(false),
            FollowLeaderType = GetArchetypeChunkComponentType<FollowLeader>(true),
            LeaderTranslation = GetComponentDataFromEntity<Translation>(true)
        };

        return job.Schedule(m_Query, inputDeps);
    }
}
[NativeDisableParallelForRestriction] [ReadOnly] public ComponentDataFromEntity<Translation> LeaderTranslation;

Remove [ReadOnly] if you using [NativeDisableParallelForRestriction] it’s doesn’t make sense :slight_smile:

This still doesn’t stop the exception.

What is the exception? Copy paste the entire exception dump if you need to.

Try setting TranslationType the [NativeDisableParallelForRestriction].

LeaderTranslation is only being read from Translation leaderTranslation = LeaderTranslation[followLeader.Leader]; but NativeArray<Translation> Translations = chunk.GetNativeArray(TranslationType); is the one being read and set by your job at Translations *= translation;.*

And it shouldn’t, it’s just note about clear code:)

And error pretty clear. For preventing memory aliasing you can’t have both - ComponentDataFromEntity and ArchetypeChunkComponentType. You should choose one. You maybe know that they not collide, but machine - no. You can replace ArchetypeChunkComponentType to ArchetypeChunkEntityType and use that entity for ComponentDataFromEntity to get translation.

The exception doesn’t let me play the game still. Wouldn’t feeding the job the list of all entities be inefficient since the job then needs to run on all entities, or will the entitiy query only run the job on the entities that match the query? If so how would I get the reference to the entity from within an IJobChunk?

And it wouldn’t until you fix that.

CDFE it’s not list of entities. It reference to “dictionary” of entities with O(1) access. Only performance hit here is random memory access.

That row don’t hint at anything? :slight_smile: job.Schedule(m_Query, inputDeps);

I already told you, same as for types for chunk but for entities

And your top part will be like that

            [ReadOnly] public ArchetypeChunkEntityType EntitiesType;

            //....other fields definitions

            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
            {
                NativeArray<Entity>       entities      = chunk.GetEntityArray(EntitiesType);
                NativeArray<FollowLeader> FollowLeaders = chunk.GetNativeArray(FollowLeaderType);

                for (int i = 0; i < chunk.Count; i++)
                {
                    Translation  translation  = LeaderTranslation[entities[i]];
                    FollowLeader followLeader = FollowLeaders[i];

                //....other things
2 Likes

Thanks for the example code. I’m confused on how I should write onto the Translation component then as I don’t get a reference to the entity but only the value. Should I use the ComponentDataFromEntity to write or should I pass in an EntityCommandBuffer as well.

ComponentDataFromEntity has setter if you sure in safety that nothing will write-write write-read same memory at same time