#if GRAPH_DESIGNER && UNITY_EDITOR /// --------------------------------------------- /// Behavior Designer /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.BehaviorDesigner.Runtime.Systems { using Opsive.BehaviorDesigner.Runtime.Components; using Opsive.BehaviorDesigner.Runtime.Groups; using Opsive.GraphDesigner.Runtime; using Opsive.GraphDesigner.Runtime.Variables.ECS; using System.Collections.Generic; using Unity.Entities; /// /// Mirrors ECS-backed SharedVariables into managed authoring SharedVariables for editor runtime inspection. /// [UpdateInGroup(typeof(TraversalSystemGroup))] [UpdateAfter(typeof(TraversalTaskSystemGroup))] [UpdateBefore(typeof(EvaluationSystem))] public partial class EditorECSSharedVariableSyncSystem : SystemBase { private readonly Dictionary m_RegistrySyncByEntity = new Dictionary(); private readonly List m_InspectedEntities = new List(); private readonly HashSet m_ActivePureECSEntities = new HashSet(); private double m_NextSyncTime; private int m_LastInspectionVersion = -1; /// /// Syncs ECS-backed SharedVariables into the managed authoring variables after traversal completes. /// protected override void OnUpdate() { if (!EditorGraphSharedVariableInspectionTracker.HasInspectedEntities) { CleanupStaleRegistries(null); return; } var inspectionVersion = EditorGraphSharedVariableInspectionTracker.InspectionVersion; var forceSync = inspectionVersion != m_LastInspectionVersion; var time = UnityEngine.Time.realtimeSinceStartupAsDouble; if (!forceSync && time < m_NextSyncTime) { return; } m_LastInspectionVersion = inspectionVersion; m_NextSyncTime = time + EditorGraphSharedVariableInspectionTracker.DefaultSyncIntervalSeconds; EditorGraphSharedVariableInspectionTracker.GetInspectedEntities(m_InspectedEntities); if (m_InspectedEntities.Count == 0) { CleanupStaleRegistries(null); return; } m_ActivePureECSEntities.Clear(); for (int i = 0; i < m_InspectedEntities.Count; ++i) { var inspectedEntity = m_InspectedEntities[i]; if (!ReferenceEquals(inspectedEntity.World, World)) { continue; } var entity = inspectedEntity.Entity; if (entity == Entity.Null || !EntityManager.Exists(entity)) { RemovePureECSSyncRegistry(entity); continue; } // Managed tree objects synchronize their own editor SharedVariable state during execution. if (BehaviorTree.GetBehaviorTree(entity) != null) { RemovePureECSSyncRegistry(entity); continue; } if (!EntityManager.HasComponent(entity) || !EntityManager.HasBuffer(entity)) { RemovePureECSSyncRegistry(entity); continue; } m_ActivePureECSEntities.Add(entity); var registry = GetOrCreateRegistry(entity); if (registry == null) { continue; } registry.SyncToManaged(World, entity); } CleanupStaleRegistries(m_ActivePureECSEntities); } /// /// Returns the cached sync context for the entity, creating it on demand when the authoring behavior tree can be resolved. /// private ECSVariableRegistry GetOrCreateRegistry(Entity entity) { if (m_RegistrySyncByEntity.TryGetValue(entity, out var registry)) { return registry; } var graphReference = EntityManager.GetComponentObject(entity); if (graphReference == null || string.IsNullOrEmpty(graphReference.AuthoringBehaviorTreeGlobalObjectId)) { return null; } var behaviorTree = BehaviorTree.ResolveBehaviorTreeFromGlobalObjectId(graphReference.AuthoringBehaviorTreeGlobalObjectId, graphReference.DesignGraphUniqueID); if (behaviorTree == null) { return null; } m_RegistrySyncByEntity[entity] = behaviorTree.CreateECSVariableSyncRegistry(World); return registry; } /// /// Removes cached contexts for entities that are no longer valid pure ECS sync targets. /// private void CleanupStaleRegistries(HashSet activePureECSEntities) { if (m_RegistrySyncByEntity.Count == 0) { return; } List staleEntities = null; foreach (var entry in m_RegistrySyncByEntity) { if (activePureECSEntities != null && activePureECSEntities.Contains(entry.Key)) { continue; } if (staleEntities == null) { staleEntities = new List(); } staleEntities.Add(entry.Key); } if (staleEntities == null) { return; } for (int i = 0; i < staleEntities.Count; ++i) { RemovePureECSSyncRegistry(staleEntities[i]); } } /// /// Removes and disposes the cached sync context for the specified entity. /// private void RemovePureECSSyncRegistry(Entity entity) { if (!m_RegistrySyncByEntity.TryGetValue(entity, out var registry)) { return; } registry?.Dispose(); m_RegistrySyncByEntity.Remove(entity); } /// /// Disposes the cached registries. /// protected override void OnDestroy() { foreach (var entry in m_RegistrySyncByEntity) { entry.Value.Dispose(); } m_RegistrySyncByEntity.Clear(); } } } #endif