Skip to content

Use Tag components to publish changes to the Bridge layer (Push Data to Managed Layer)

What

There are some instances of our non-refactored Systems mixing the manipulation of managed and unmanaged code.

    public sealed partial class ArmyPathRendererSystem : SystemBase {
        protected override void OnUpdate() {
            using NativeList<ArmyPathToUpdateContainer> armiesToUpdateLine = new(Allocator.Temp);
            using NativeArray<Entity> armyEntities = _query.ToEntityArray(Allocator.Temp);
            // Unmanaged part
            for (int entityIndex = 0; entityIndex < armyEntities.Length; ++entityIndex) {

                // complex unmanaged code filtering entities

                armiesToUpdateLine.Add(armyEntities[entityIndex]);
            }

            // Managed part
            foreach (ArmyPathToUpdateContainer armyPathContainer in armiesToUpdateLine) {
                GameObject gameObject;
                if (EntityManager.HasComponent<ArmyPathRendererComponent>(armyPathContainer.ArmyEntity)) {
                    gameObject = EntityManager.GetComponentData<ArmyPathRendererComponent>(armyPathContainer.ArmyEntity).Value;
                } else {
                    gameObject = Object.Instantiate(armyPathRendererConfig.LineRendererPrefab);
                    EntityManager.AddComponent<ArmyPathRendererComponent>(armyPathContainer.ArmyEntity);
                    EntityManager.SetComponentData(armyPathContainer.ArmyEntity, new ArmyPathRendererComponent { Value = gameObject });
                }

                ArmyPathLineRenderer armyPathLineRenderer = gameObject.GetComponent<ArmyPathLineRenderer>();
                armyPathLineRenderer.UpdateLine(armyPathContainer.MoveCommandPositions, zoomLevel.Value);
            }
        }
    }

Why

Besides going against the dependency flow of the architecture, it also prevents the System from being Burst-compatible as it references managed code.

How

Instead of updating the managed part directly in the DOTS Layer, we should mark those entities for update somehow. Tag components are an easy way of doing that:

    public struct ArmyPathLineRendererUpdateTag : IComponentData { }

    public partial struct ArmyPathRendererSystem : ISystem {
        [BurstCompile]
        public void OnUpdate(ref SystemState state) {
            using NativeList<ArmyPathToUpdateContainer> armiesToUpdateLine = new(Allocator.Temp);

            // ...

            foreach (ArmyPathToUpdateContainer armyPathContainer in armiesToUpdateLine) {
                EntityManager.AddComponent<ArmyPathLineRendererUpdateTag>(armyPathContainer.ArmyEntity);
            }
        }
    }

Then we can move the managed part to our Bridge Layer:

    public partial class ArmyPathRendererBridgeSystem : SystemBase, IArmyPathRendererUpdateNotifier {
        public event IArmyPathRendererUpdateNotifier.PathUpdatedHandler? OnPathUpdated;

        protected override void OnCreate() {
            RequireForUpdate(
                new EntityQueryBuilder(Allocator.Temp)
                    .WithAny<ArmyPathLineRendererUpdateTag>()
                    .Build(this)
            );
        }

        protected override void OnUpdate() {
            if (OnPathUpdated == null) {
                return;
            }

            foreach ((ArmyPathRendererComponent armyPathRenderer, Entity entity) in SystemAPI
                         .Query<ArmyPathRendererComponent>()
                         .WithAny<ArmyPathLineRendererUpdateTag, ArmyPathLineRendererRemoveTag>()
                         .WithEntityAccess()) {
                // ...
                // Raise the event to be handled in the Managed Layer
                OnPathUpdated.Invoke();
            }

            // Remove the ArmyPathLineRendererUpdateTag since we've already reacted to the change
            EntityQuery pathRendererUpdateTagRemoveQuery =
                SystemAPI.QueryBuilder().WithAll<ArmyPathLineRendererUpdateTag>().Build();
            entityManager.RemoveComponent<ArmyPathLineRendererUpdateTag>(pathRendererUpdateTagRemoveQuery);
        }
    }
If the changes are expected happen very frequently, consider user Enableable Components for increased performance.