Is it possible to have a Dynamicbuffer of Icomponent tags which can be set as components?

I’ve tried to do this but I’m not sure if I’m doing it right or whether its possible. I’m guessing it isn’t possible because a dynamicbuffer is attached to an entity, I cant figure out how to access and add that as an component from that entity.

Or maybe this isn’t a good way to do what I want. I figured I could have it dynamically create 500 or so component tags and add them to core entities and then add that component tag to all the surrounding entity’s that are within a certain distant. A sort of quick way to have a list of entities that are with a certain distance. I’m not sure if I’ve explained it well enough but that’s the gist of it.

I’ve done something similar. Here’s the code if it helps. Explanation is below the code.

public class AllyProximitySystem : JobComponentSystem
{
    private EntityQuery dalmatianProximityUsers;
    private EntityQuery epiroteProximityUsers;

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var awaredAlliesFromEntity =
            GetBufferFromEntity<AllyWithinAwarenessRangeElement>(false);
        var speakingAlliesFromEntity =
            GetBufferFromEntity<AllyWithinSpeakingRangeElement>(false);

        var bodyHandles = new NativeArray<JobHandle>(4, Allocator.Temp);
        var dalmatianAlliesTransforms =
            dalmatianProximityUsers.ToComponentDataArray<LocalToWorld>(
                Allocator.TempJob,
                out var dalmatianAlliesTransformsJobHandle
            );
        var dalmatianAlliesEntities =
            dalmatianProximityUsers.ToEntityArray(
                Allocator.TempJob,
                out var dalmatianAlliesEntitiesJobHandle
            );
        var epiroteAlliesTransforms =
            epiroteProximityUsers.ToComponentDataArray<LocalToWorld>(
                Allocator.TempJob,
                out var epiroteAlliesTransformsJobHandle
            );
        var epiroteAlliesEntities =
            dalmatianProximityUsers.ToEntityArray(
                Allocator.TempJob,
                out var epiroteAlliesEntitiesJobHandle
            );

        bodyHandles[0] = dalmatianAlliesTransformsJobHandle;
        bodyHandles[1] = dalmatianAlliesEntitiesJobHandle;
        bodyHandles[2] = epiroteAlliesEntitiesJobHandle;
        bodyHandles[3] = epiroteAlliesTransformsJobHandle;
        inputDeps = JobHandle.CombineDependencies(
            JobHandle.CombineDependencies(bodyHandles), inputDeps
        );
        bodyHandles.Dispose();

        inputDeps = new WithinRangeJob
        {
            alliesTransforms = dalmatianAlliesTransforms,
            alliesEntities = dalmatianAlliesEntities,
            awaredAlliesFromEntity = awaredAlliesFromEntity,
            speakingAlliesFromEntity = speakingAlliesFromEntity
        }.ScheduleSingle(dalmatianProximityUsers, inputDeps);
        inputDeps = new WithinRangeJob
        {
            alliesTransforms = epiroteAlliesTransforms,
            alliesEntities = epiroteAlliesEntities,
            awaredAlliesFromEntity = awaredAlliesFromEntity,
            speakingAlliesFromEntity = speakingAlliesFromEntity
        }.ScheduleSingle(epiroteProximityUsers, inputDeps);

        return inputDeps;
    }

   [BurstCompile]
    private struct WithinRangeJob : IJobForEachWithEntity<LocalToWorld, AllyProximity>
    {
        [ReadOnly]
        [DeallocateOnJobCompletion]
        public NativeArray<LocalToWorld> alliesTransforms;
        [ReadOnly]
        [DeallocateOnJobCompletion]
        public NativeArray<Entity> alliesEntities;

        [NativeDisableParallelForRestriction]
        public BufferFromEntity<AllyWithinAwarenessRangeElement> awaredAlliesFromEntity;
        [NativeDisableParallelForRestriction]
        public BufferFromEntity<AllyWithinSpeakingRangeElement> speakingAlliesFromEntity;

        public void Execute(
            Entity entity,
            int jobIndex,
            ref LocalToWorld transform,
            ref AllyProximity proximityStats
        )
        {
            var awaredAllies = awaredAlliesFromEntity[entity];
            var awaredAlliesLength = awaredAllies.Length;
            var awaredAlliesEntities =
                new NativeArray<Entity>(awaredAlliesLength,Allocator.Temp);
            for (var i = 0; i < awaredAlliesLength; i++)
            {
                awaredAlliesEntities[i] = awaredAllies[i].ally;
            }

            var speakingAllies = speakingAlliesFromEntity[entity];
            var speakingAlliesLength = speakingAllies.Length;
            var speakingAlliesEntities =
                new NativeArray<Entity>(speakingAlliesLength, Allocator.Temp);
            for (var i = 0; i < speakingAlliesLength; i++)
            {
                speakingAlliesEntities[i] = speakingAllies[i].entity;
            }

            var alliesTransformsLength = alliesTransforms.Length;
            for(var i = 0; i < alliesTransformsLength; i++)
            {
                var allyEntity = alliesEntities[i];
                var allyAwareness = awaredAlliesFromEntity[allyEntity];
                var allySpeaking = speakingAlliesFromEntity[allyEntity];

                if (entity.Equals(allyEntity))
                {
                    continue;
                }
                var allyTransform = alliesTransforms[i];
                var withinAwarenessRadius =
                    allyTransform.Position.DistanceFrom(transform.Position)
                    < proximityStats.awarenessRadius;
                var awarenessContainsThis =
                    awaredAlliesEntities.Contains(allyEntity);
                if (withinAwarenessRadius
                    && !awarenessContainsThis
                )
                {
                    awaredAllies.Add(new AllyWithinAwarenessRangeElement
                    {
                        ally = allyEntity
                    });
                    allyAwareness.Add(new AllyWithinAwarenessRangeElement
                    {
                        ally = entity
                    });
                }
                else if (!withinAwarenessRadius
                    && awarenessContainsThis
                )
                {
                    awaredAllies.Remove(new AllyWithinAwarenessRangeElement
                    {
                        ally = allyEntity
                    });
                    allyAwareness.Remove(new AllyWithinAwarenessRangeElement
                    {
                        ally = entity
                    });
                }

                var withinSpeakingRadius =
                    allyTransform.Position.DistanceFrom(transform.Position)
                    < proximityStats.speakingRadius;
                var speakingContainsThis =
                    speakingAlliesEntities.Contains(allyEntity);
                if (withinSpeakingRadius
                    && !speakingContainsThis
                )
                {
                    speakingAllies.Add(new AllyWithinSpeakingRangeElement
                    {
                        entity = allyEntity
                    });
                    allySpeaking.Add(new AllyWithinSpeakingRangeElement
                    {
                        entity = entity
                    });
                }
                else if (!withinSpeakingRadius
                    && speakingContainsThis
                )
                {
                    speakingAllies.Remove(new AllyWithinSpeakingRangeElement
                    {
                        entity = allyEntity
                    });
                    allySpeaking.Remove(new AllyWithinSpeakingRangeElement
                    {
                        entity = entity
                    });
                }
            }
        }
    }

    protected override void OnCreate()
    {
        dalmatianProximityUsers = GetEntityQuery(new EntityQueryDesc
        {
            All = new ComponentType[]
            {
                ComponentType.ReadOnly<DalmatianTag>(),
                ComponentType.ReadOnly<AllyProximity>(),
                ComponentType.ReadOnly<LocalToWorld>(),
                ComponentType.ReadOnly<Translation>()
            },
        });
        dalmatianProximityUsers.SetFilterChanged(typeof(Translation));

        epiroteProximityUsers = GetEntityQuery(new EntityQueryDesc
        {
            All = new ComponentType[]
            {
                ComponentType.ReadOnly<EpiroteTag>(),
                ComponentType.ReadOnly<AllyProximity>(),
                ComponentType.ReadOnly<LocalToWorld>(),
                ComponentType.ReadOnly<Translation>()
            },
        });
        epiroteProximityUsers.SetFilterChanged(typeof(Translation));
    }
}

You set the change filter on Translation, because Unity’s TRSToLocalToWorldSystem constantly writes Translation to LocalToWorld, but Translation is written to only if one of your systems do. This way the system only activates when an entity moves. Once it does it checks all other “allied” entities to see if they’re still within range, and update the buffer of allied entities accordingly. Your use case is different since you only want “core entities” to detect within range entities, so modify the system above to have two entity queries - one for core entities, and one for within range entities. The job would then cycle through core entities and alliesTransforms and alliesEntities would be those of your non-core entities.

I understand that keeping track of entities within range of another entity is still extra work, but it reduces code overhead and decouples systems that rely on entities within range from systems that maintain entities within range. You’d only have to have a BufferFromEntity in the depending system instead of calculating all allies within range every frame.

1 Like

Wow, that’s great, thanks. It will take me a bit to parse that code as I’m still trying to figure out how exactly jobcomponentsystems work to be honest, the docs and examples arent that much help and very few tutorials exist. But I guess that’s what you get when you try using something that is still in early development, thanks again.