How to instantiate an entity and reference it on the same job?

So I’m making an RTS with ECS, and like every other RTS, you can move units by setting a target position where the unit should go.

The idea is to have a Unit Entity and a Target Visualizer Entity for each Unit, the problem lies on the creation of this visualizer, i have a prefab with the visualizer graphics and i want to instantiate it on unit creation. Something like this:

    [UpdateInGroup(typeof(InitializationSystemGroup))]
    public partial struct TargetInitializationSystem : ISystem
    {
        public Entity targetPrefab;
        public EntityQuery _query;

        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
            _query = new EntityQueryBuilder(Allocator.Temp)
                .WithDisabled<TargetData, TargetVisualizerData>()
                .Build(ref state);
            state.RequireForUpdate<PrefabReference>();
            
            state.RequireForUpdate<EndInitializationEntityCommandBufferSystem.Singleton>();

            if (targetPrefab == Entity.Null)
            {
                var prefabs = SystemAPI.GetSingletonBuffer<PrefabReference>();

                foreach (var prefab in prefabs)
                {
                    if (prefab.name != "target") continue;

                    targetPrefab = prefab.prefab;
                    break;
                }

                Debug.Assert(targetPrefab != Entity.Null, "target prefab reference was not found.");
        }

        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            var ecbs = SystemAPI.GetSingleton<EndInitializationEntityCommandBufferSystem.Singleton>();

            }

            new InstantiateTargetJob()
            {
                prefab = targetPrefab,
                ecb = ecbs.CreateCommandBuffer(state.WorldUnmanaged)
            }.Schedule(_query, state.Dependency).Complete();
        }

        public partial struct InstantiateTargetJob : IJobEntity
        {
            [ReadOnly] public Entity prefab;
            public EntityCommandBuffer ecb;

            public void Execute(Entity entity, ref TargetVisualizerData visualizerData)
            {
                if (visualizerData.VisualizerEntity != Entity.Null) return;
                
                ecb.SetComponentEnabled<TargetVisualizerData>(entity, false);
                
                visualizerData.VisualizerEntity = ecb.Instantiate(prefab);
            }
        }
    }


[...]

    public struct TargetVisualizerData : IComponentData, IEnableableComponent
    {
        public Entity VisualizerEntity;
    }

When i go to set the visualizerData’s entity reference it is set as Invalid Entity, from what i assume, the ecb.Instantiate(prefab) is not being playedback so the reference comes as Invalid Entity during playtime. But i dont see any other way that i can instantiate a prefab and set a reference to it on the same job…

image

Any idea how i can set a reference to an entity on the same Job, or maybe how to, after set the reference after ECB-Playback.

Entities scheduled for creation by EntityCommandBuffer don’t exist by the point the command buffer returns from the Instantiate call, so it instead returns an Entity value that only makes sense within the context of the EntityCommandBuffer, and must be replaced by one of the playback commands if you want a valid Entity reference later on. More info here.

The gist of this is that any time you create a reference to an entity instantiated from EntityCommandBuffer, you need to produce references to it through the EntityCommandBuffer via SetComponent etc. instead of direct assignment. In your code, where you’re setting the field on line 57, you would instead modify a local copy of the component with read-only access (or just use the ref, even though the value will be replaced by the command buffer) and then use EntityCommandBuffer.SetComponent to schedule this new value for assignment.

There’s a caveat that changes made to this component between this job and the playback of the ECB would be lost since you’re setting the entire component, and the ECB’s changes could be overridden by other ECBs that play later, so you need to make sure the jobs are designed and ordered such that you have a usable, consistent output.

That did the trick, didnt expect ecb.SetComponent to save the reference. Thank you!

                ecb.SetComponent(entity, new TargetVisualizerData(){ VisualizerEntity = ecb.Instantiate(prefab)});

For anyone who needs it