Best way to obtain data in MonoBehaviour from instantiated entities in ECS code

Hello,

I am currently working on a Hexagon Map project. There is a map with tiles, and when the player zooms in, the tiles spawn smaller tiles sort of like a “zoom in”.

I am currently generating the colliders of the smaller tiles through entities instantiated from a Command Buffer on a ISystem.

Now I want to do the graphics rendering part. I am using GPUInstancing.

What I want, is to grab the data in the components created from the command buffer to do the rendering.

What I need, is that the ISystem creates the ComponentData, stores it somewhere, and I can get that data for GPUInstancing from Monobehaviour at runtime.

Since entities are being spawned and destroyed all the time due to distance, I can’t keep track of a fixed set of entities. Also, there are larger WorldMap tiles, and smaller Overworld tiles. I only need the smaller overworld tiles.

I have to somehow, when I give the call, get all the “WorldMapTileData” components of the entities that also have the “OverworldTileTag” component, and process them to render.

This is the code I have for the Update on the ISystem:

    public void OnUpdate(ref SystemState state)
    {
        // Only run system if world map systems are active.
        bool worldMapSystems = false;
        Entity rulesEntity = Entity.Null;
        foreach (var dataHolder in SystemAPI.Query<GameWorldEntityHolderData>())
        {
            rulesEntity = dataHolder.rulesEntity;
            worldMapSystems = dataHolder.worldMapSystems;
        }
        if (!worldMapSystems) return;

        // Grab command buffer for entity creation / modification / destruction
        EndSimulationEntityCommandBufferSystem.Singleton commandBufferSystem = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();

        #region WORLD TILE LEVELS
        // Grab camera entity
        Entity cameraEntity = Entity.Null;
        foreach (var camera in SystemAPI.Query<WorldCameraTag>())
            cameraEntity = camera.cameraEntity;
        var worldMapRulesEntityLookup = state.GetComponentLookup<GameWorldEntityHolderData>(true);
        GameWorldEntityHolderData rulesData = worldMapRulesEntityLookup[rulesEntity];

        // Get Current World Map Tile Position and World Map Render state
        var localTransforms = state.GetComponentLookup<LocalTransform>(true);
        var worldMapTilesData = state.GetComponentLookup<WorldMapTileData>(true);
        var overworldTileTags = state.GetComponentLookup<OverworldTileTag>(true);
        WorldMapCameraPositionSystem worldMapCameraPositionSystem = new()
        {
            cameraEntity = cameraEntity,
            localTransforms = localTransforms,
            physicsWorld = SystemAPI.GetSingleton<PhysicsWorldSingleton>(),
            worldMapTilesData = worldMapTilesData,
            overworldTileTags = overworldTileTags
        };
        state.Dependency = worldMapCameraPositionSystem.Schedule(state.Dependency);
        state.Dependency.Complete();

        // World Map Render update
        worldMapRulesEntityLookup = state.GetComponentLookup<GameWorldEntityHolderData>(true);
        rulesData = worldMapRulesEntityLookup[rulesEntity];

        // Flag WorldMapTiles to render Whole or Split
        WorldMapTileTargetRenderSystem worldMapTileTargetRenderSystem = new()
        {
            rulesDataLookup = worldMapRulesEntityLookup,
        };
        state.Dependency = worldMapTileTargetRenderSystem.ScheduleParallel(state.Dependency);

        // Split flagged WorldMapTiles
        WorldMapTileRenderSystem worldMapTileRenderSystem = new()
        {
            commandBuffer = commandBufferSystem.CreateCommandBuffer(state.WorldUnmanaged),
            rulesData = rulesData,
        };
        state.Dependency = worldMapTileRenderSystem.Schedule(state.Dependency);

        // Destroy distant overworld tiles
        OverworldTileDistantClearSystem overworldTileDistantClearSystem = new()
        {
            commandBuffer = commandBufferSystem.CreateCommandBuffer(state.WorldUnmanaged),
            rulesDataLookup = worldMapRulesEntityLookup,
        };
        state.Dependency = overworldTileDistantClearSystem.Schedule(state.Dependency);

        // Flag OverworldTiles to render whole or split
        OverworldTileTargetRenderSystem overworldTileTargetRenderSystem = new()
        {
            rulesDataLookup = worldMapRulesEntityLookup,
        };
        state.Dependency = overworldTileTargetRenderSystem.ScheduleParallel(state.Dependency);

        // Split flagged WorldMapTiles
        OverworldTileRenderSystem overworldTileRenderSystem = new()
        {
            commandBuffer = commandBufferSystem.CreateCommandBuffer(state.WorldUnmanaged),
            rulesData = rulesData,
        };
        state.Dependency = overworldTileRenderSystem.Schedule(state.Dependency);

        // Destroy distant WorldPlace tiles
        WorldPlaceTileDistantClearSystem worldPlaceTileDistantClearSystem = new()
        {
            commandBuffer = commandBufferSystem.CreateCommandBuffer(state.WorldUnmanaged),
            rulesDataLookup = worldMapRulesEntityLookup,
        };
        state.Dependency = worldPlaceTileDistantClearSystem.Schedule(state.Dependency);

        // World Map Update done 
        WorldMapRulesUpdateDoneSystem worldMapRulesUpdateDoneSystem = new()
        {
        };
        state.Dependency = worldMapRulesUpdateDoneSystem.Schedule(state.Dependency);
        #endregion
    }

This is the code the Instantiates the new smaller tiles and the components I need to access later:

    [BurstCompile]
    private partial struct WorldMapTileRenderSystem : IJobEntity
    {
        public EntityCommandBuffer commandBuffer;
        public GameWorldEntityHolderData rulesData;

        public void Execute(ref WorldMapTileData worldMapTileData)
        {
            // Skip if no change is needed
            if (worldMapTileData.tileRender == worldMapTileData.targetRender) return;
            //Debug.Log("World map tile " + worldMapTileData.index + " updating render to " + worldMapTileData.targetRender);
            Entity tileInstance;
            WorldMapTileData tileData;
            Entity gameTileEntity = rulesData.overworldTilePrefab;
            float3 position;
            quaternion rotation = quaternion.LookRotation(Vector3.right, Vector3.up);
            if (worldMapTileData.targetRender == WorldMapTileRenderSplit)
            {
                for (int hexa = 0; hexa < HexasPerHexa; hexa++)
                {
                    // Instantiate World Tile Entities with Mesh Collider
                    tileInstance = this.commandBuffer.Instantiate(gameTileEntity);

                    // Set and Add WorldTileData
                    tileData = GetDefaultOverworldTileData(hexa, tileInstance);
                    tileData.worldMapRulesEntity = worldMapTileData.worldMapRulesEntity;
                    tileData.tileRenderType = (ushort) OverworldRender;

                    this.commandBuffer.AddComponent(tileInstance, tileData);
                    this.commandBuffer.AddComponent(tileInstance, new OverworldTileTag { worldMapTile = worldMapTileData.index });

                    // Set Position and Rotation.
                    position = GetWorldMapTileHexaPosition(worldMapTileData.index, hexa, rulesData.worldX, rulesData.worldZ);
                    position = new()
                    {
                        x = position.x,
                        y = position.y + -6f,
                        z = position.z
                    };

                    // Set LocalTransform
                    this.commandBuffer.SetComponent(tileInstance,
                        LocalTransform.FromPositionRotation(position, rotation));
                }

                //Debug.Log("World Map Tile " + worldMapTileData.index + " has spawned overworld tiles");
            }

            worldMapTileData.tileRender = worldMapTileData.targetRender;
        }
    }

There are lots of WorldMapTileData going around. Most of them belonging to the larger original WorldMap tiles. The new ones have a “OverworldTileTag” to flag them as the smaller ties. So I don’t need all the “WorldMapTileData” components, as that would include also the larger tiles. I just need the WorldMapTileData components created for the newly instantiated overworld tiles.

How can I go about this?

Thank you for your time and help.