Tried using sharedcomponentfilter, no clue why this query isn't working

I’ve been spamming the Discord about this. I’ve pretty much given up on this implementation but I’d still like to know where I went wrong.

In my map generator, I am first splitting up the map into fragments. I create an entity for that fragment, and immediately after, create a grid of cells for that fragment. Each cell is an entity, and I was assigning a SharedComponent to those cells that had the fragment entity as it’s only field (I later tried it just with the index, same deal). This was all in order to help me with generation down the line.

code snippet:

var newFragmentEntity = ecb.CreateEntity();
                        ecb.AddComponent(newFragmentEntity, mapGridFragment);
                        //Create cell entities within the new fragment
                        var sharedFragmentComponent = new Map.CellFragmentShared()
                        {
                            FragmentEntityIndex = newFragmentEntity.Index
                        };

                        for (int i = 0; i < mapGridFragment.TotalCells; i++)
                        {
                            var cellPosition = mapGridFragment.CalculateCellWorldPosition(i);

                            //This could get fucked up if the cell size changes.
                            var cellCoordinate = new int3((int)(cellPosition.x / halfCellSize), (int)(cellPosition.y / halfCellSize),
                                (int)(cellPosition.z / halfCellSize));

                            if (cellPosition.y > mapGenProperties.GroundLevel)
                            {
                                var newCell = new Map.Cell()
                                {
                                    Position = cellPosition,
                                    GlobalCoordinate = cellCoordinate,
                                    Size = cellSize,
                                    CellType = GridCellType.None
                                };

                                var newCellEntity = ecb.CreateEntity();
                                ecb.AddComponent(newCellEntity, newCell);
                                ecb.AddSharedComponent(newCellEntity, sharedFragmentComponent);
                            }
                        }

The next system looks for fragments that need to have their cells initialized. I have 2 queries.

  1. Fragment Query
  2. Cells Query (General query for all cell entities)

In this system, I first query for all fragments that still need their cells initialized. I pick the first one at index 0. And here’s where it all goes wrong.

I want to then filter the cells query based on the fragment I’m operating on currently. So I can limit the amount of entities I need to iterate through. I take the fragement entity I am working on, and use that to set the SharedComponentFilter.

code:

public void OnCreate(ref SystemState state)
        {
            _fragmentsToInitializeQuery = SystemAPI.QueryBuilder().WithAll<Map.MapGridFragment>().WithNone<Map.FragmentInitialized>().Build();

            _cellsQuery = SystemAPI.QueryBuilder().WithAll<Map.Cell, Map.CellFragmentShared>().WithNone<Map.CellIsSet>().Build();
            
            state.RequireForUpdate(_fragmentsToInitializeQuery);
            state.RequireForUpdate<Map.GeneratorProperties>();
        }

        public void OnUpdate(ref SystemState state)
        {
            var ecb = SystemAPI.GetSingleton<BeginInitializationEntityCommandBufferSystem.Singleton>()
                .CreateCommandBuffer(state.WorldUnmanaged);
            var mapGenProperties = SystemAPI.GetSingleton<Map.GeneratorProperties>();

            //pick the first fragement out of the initializable fragements...
            var fragmentEntities = _fragmentsToInitializeQuery.ToEntityArray(Allocator.Temp);
            var fragmentEntity = fragmentEntities[0];
            
            //if it's a new fragement, reset the counter. 
            if (_currentFragment != fragmentEntity)
            {
                _placementAttempts = 0;
                _currentFragment = fragmentEntity;
                Debug.Log(fragmentEntity.Index);
            }

            //var fragmentSharedComponent =
              //  state.EntityManager.GetSharedComponent<Map.CellFragmentShared>(fragmentEntity);
            
            //Set the shared component filter on the elements.
            _cellsQuery.ResetFilter();
            _cellsQuery.SetSharedComponentFilter<Map.CellFragmentShared>(new Map.CellFragmentShared(){FragmentEntityIndex = fragmentEntity.Index});

            //Don't move on if there are not entities in this query. Breaks System. 
            if (_cellsQuery.IsEmpty)
                return;

it fails here. No matter what I try. Once I apply that filter to the cell query it returns empty every time. As you can see by the commented out code, I tried adding that same exact shared component instance to the fragment entity, getting a reference to it, and using that as the filter paremeter. that didn’t help. As I alluded to, I tried simplifying the field in the component to just the entity index, that didn’t work. I tried writing my own equatable, that didn’t work either.

I’m logging out the fragment entity that I’m working on, I can see it’s index and version number. In the inspector I can see cell entities that have shared components that match that fragment entity value!

As soon as I remove the filter, all of my cell entities pop up in the query, so it’s not like there’s an error elsewhere in my entity query.

Thank you for reading, I’m losing my mind.

so it appears to be related to the fact that I was using the entity reference that the ecb was returning for the shared component value. I’m assuming that that is a temporary entity reference that no longer exists once the ecb is run? I’m not sure. When I used the entity struct itself, I could see the entity index clear as day in the shared component, and it matched the created fragment entity index.

I double checked in the inspector when I was assigning the entity index instead of the entity struct itself to the shared component, and all the values were set to -1.

Anyway when I instead used a custom value, in this case a hash code created from the fragement float3 origin position that I knew would be unique, that ended up working.

Yeah, EntityCommandBuffer’s CreateEntity and Instantiate methods return temporary Entity keys only valid in the context of the ECB. The proper way to write these to component data is indeed to write the Entity value in its entirety as an Entity field (can be nested anywhere) through ECB methods like SetComponent/SetSharedComponent, which cause internal remapping to occur on playback.

1 Like