com.artromskiy.deltaecs.unity 0.0.65 → 0.0.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/Editor/HierarchyNodeHandler.cs +224 -46
- package/README.md +1 -1
- package/Runtime/SceneAuthoring.cs +25 -0
- package/Runtime/SceneWorld.cs +91 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -22,9 +22,12 @@ namespace Delta.ECS.Unity.Editor
|
|
|
22
22
|
private static readonly SchemaId HiddenEntitySchema = new(ViewComponentSchemaIds.HideFromEntityHierarchy);
|
|
23
23
|
private static double _nextRuntimeHierarchyRefresh;
|
|
24
24
|
private static bool _wasPlaying;
|
|
25
|
+
private static bool _authoringsDirty = true;
|
|
26
|
+
private static SceneAuthoring[] _authorings = Array.Empty<SceneAuthoring>();
|
|
25
27
|
private static readonly HashSet<HierarchyNodeHandler> Instances = new();
|
|
26
28
|
|
|
27
29
|
private readonly Dictionary<EntityKey, EntityNode> _entityNodes = new();
|
|
30
|
+
private readonly Dictionary<SceneAuthoring, RuntimeHierarchyState> _runtimeStates = new();
|
|
28
31
|
private readonly HashSet<EntityKey> _activeEntityKeys = new();
|
|
29
32
|
private readonly List<EntityKey> _removedEntityKeys = new();
|
|
30
33
|
private readonly Dictionary<HierarchyNode, NodeReference> _nodes = new();
|
|
@@ -39,6 +42,8 @@ namespace Delta.ECS.Unity.Editor
|
|
|
39
42
|
HierarchyWindow.BindView += BindHierarchyView;
|
|
40
43
|
EditorApplication.delayCall += BindExistingHierarchyViews;
|
|
41
44
|
EditorApplication.update += UpdateRuntimeHierarchy;
|
|
45
|
+
SceneAuthoring.HierarchyChanged += InvalidateAuthorings;
|
|
46
|
+
SceneWorld.LiveWorldsChanged += InvalidateAuthorings;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
public override string GetNodeTypeName() => nameof(HierarchyNodeHandler);
|
|
@@ -98,6 +103,11 @@ namespace Delta.ECS.Unity.Editor
|
|
|
98
103
|
pair.Key.SceneData.Changed -= pair.Value;
|
|
99
104
|
}
|
|
100
105
|
}
|
|
106
|
+
foreach (RuntimeHierarchyState state in _runtimeStates.Values)
|
|
107
|
+
{
|
|
108
|
+
state.Dispose();
|
|
109
|
+
}
|
|
110
|
+
_runtimeStates.Clear();
|
|
101
111
|
_sceneDataSubscriptions.Clear();
|
|
102
112
|
_loadedAuthorings.Clear();
|
|
103
113
|
_removedAuthorings.Clear();
|
|
@@ -150,6 +160,8 @@ namespace Delta.ECS.Unity.Editor
|
|
|
150
160
|
|
|
151
161
|
internal static void RefreshAll()
|
|
152
162
|
{
|
|
163
|
+
InvalidateAuthorings();
|
|
164
|
+
GetAuthorings();
|
|
153
165
|
var changed = false;
|
|
154
166
|
foreach (var instance in Instances)
|
|
155
167
|
{
|
|
@@ -206,7 +218,22 @@ namespace Delta.ECS.Unity.Editor
|
|
|
206
218
|
}
|
|
207
219
|
|
|
208
220
|
_nextRuntimeHierarchyRefresh = EditorApplication.timeSinceStartup + RuntimeHierarchyRefreshInterval;
|
|
209
|
-
|
|
221
|
+
if (_authoringsDirty)
|
|
222
|
+
{
|
|
223
|
+
RefreshAll();
|
|
224
|
+
}
|
|
225
|
+
else
|
|
226
|
+
{
|
|
227
|
+
bool changed = false;
|
|
228
|
+
foreach (HierarchyNodeHandler instance in Instances)
|
|
229
|
+
{
|
|
230
|
+
changed |= instance.RefreshRuntimeChanges();
|
|
231
|
+
}
|
|
232
|
+
if (changed)
|
|
233
|
+
{
|
|
234
|
+
EditorApplication.RepaintHierarchyWindow();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
210
237
|
return;
|
|
211
238
|
}
|
|
212
239
|
|
|
@@ -217,6 +244,18 @@ namespace Delta.ECS.Unity.Editor
|
|
|
217
244
|
}
|
|
218
245
|
}
|
|
219
246
|
|
|
247
|
+
private static void InvalidateAuthorings() => _authoringsDirty = true;
|
|
248
|
+
|
|
249
|
+
private static SceneAuthoring[] GetAuthorings()
|
|
250
|
+
{
|
|
251
|
+
if (_authoringsDirty)
|
|
252
|
+
{
|
|
253
|
+
_authorings = Resources.FindObjectsOfTypeAll<SceneAuthoring>();
|
|
254
|
+
_authoringsDirty = false;
|
|
255
|
+
}
|
|
256
|
+
return _authorings;
|
|
257
|
+
}
|
|
258
|
+
|
|
220
259
|
private static void BindHierarchyView(HierarchyWindow window, HierarchyView view)
|
|
221
260
|
{
|
|
222
261
|
if (view == null)
|
|
@@ -432,19 +471,46 @@ namespace Delta.ECS.Unity.Editor
|
|
|
432
471
|
}
|
|
433
472
|
}
|
|
434
473
|
|
|
435
|
-
private bool Reconcile()
|
|
474
|
+
private bool Reconcile(bool changed = false)
|
|
436
475
|
{
|
|
437
476
|
if (!Hierarchy.IsCreated)
|
|
438
477
|
{
|
|
439
478
|
return false;
|
|
440
479
|
}
|
|
441
480
|
|
|
442
|
-
var changed = false;
|
|
443
481
|
_activeEntityKeys.Clear();
|
|
444
482
|
|
|
445
483
|
var sceneHandler = Hierarchy.GetNodeTypeHandler<HierarchySceneHandler>();
|
|
446
|
-
var authoringsInScenes =
|
|
484
|
+
var authoringsInScenes = GetAuthorings();
|
|
447
485
|
SynchronizeSceneSubscriptions(authoringsInScenes);
|
|
486
|
+
foreach (var pair in _runtimeStates)
|
|
487
|
+
{
|
|
488
|
+
if (!EditorApplication.isPlaying || !pair.Value.World.IsBound
|
|
489
|
+
|| !SceneWorld.TryFindLiveWorld(pair.Key.SceneData, out SceneWorld liveWorld)
|
|
490
|
+
|| !ReferenceEquals(liveWorld, pair.Value.World))
|
|
491
|
+
{
|
|
492
|
+
_removedAuthorings.Add(pair.Key);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
foreach (SceneAuthoring authoring in _removedAuthorings)
|
|
496
|
+
{
|
|
497
|
+
_runtimeStates[authoring].Dispose();
|
|
498
|
+
_runtimeStates.Remove(authoring);
|
|
499
|
+
}
|
|
500
|
+
_removedAuthorings.Clear();
|
|
501
|
+
// Runtime nodes survive authored-data reconciliation. Their own dirty queue
|
|
502
|
+
// removes destroyed, hidden or newly bound entities below.
|
|
503
|
+
foreach (var pair in _entityNodes)
|
|
504
|
+
{
|
|
505
|
+
NodeReference reference = pair.Value.Reference;
|
|
506
|
+
if (reference.IsRuntimeOnly && reference.RuntimeWorld.IsBound
|
|
507
|
+
&& _loadedAuthorings.Contains(reference.Authoring)
|
|
508
|
+
&& _runtimeStates.TryGetValue(reference.Authoring, out RuntimeHierarchyState state)
|
|
509
|
+
&& ReferenceEquals(state.World, reference.RuntimeWorld))
|
|
510
|
+
{
|
|
511
|
+
_activeEntityKeys.Add(pair.Key);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
448
514
|
for (var i = 0; i < authoringsInScenes.Length; i++)
|
|
449
515
|
{
|
|
450
516
|
var authoringHost = authoringsInScenes[i];
|
|
@@ -464,6 +530,7 @@ namespace Delta.ECS.Unity.Editor
|
|
|
464
530
|
{
|
|
465
531
|
SceneWorld.TryFindLiveWorld(authoringHost.SceneData, out runtimeWorld);
|
|
466
532
|
}
|
|
533
|
+
runtimeWorld?.RefreshDestroyedViews();
|
|
467
534
|
|
|
468
535
|
var entities = authoringHost.SceneData.Entities;
|
|
469
536
|
for (var entityIndex = 0; entityIndex < entities.Count; entityIndex++)
|
|
@@ -484,8 +551,7 @@ namespace Delta.ECS.Unity.Editor
|
|
|
484
551
|
: default;
|
|
485
552
|
bool hasView = authoringHost.TryGetView(authoring.StableId, out _)
|
|
486
553
|
|| (runtimeWorld != null
|
|
487
|
-
&& runtimeWorld.
|
|
488
|
-
&& runtimeWorld.TryGetView(runtimeEntity, out _));
|
|
554
|
+
&& runtimeWorld.HasRuntimeView(runtimeEntity));
|
|
489
555
|
if (hasView || IsHiddenFromHierarchy(authoring, runtimeWorld, runtimeEntity))
|
|
490
556
|
{
|
|
491
557
|
continue;
|
|
@@ -556,27 +622,7 @@ namespace Delta.ECS.Unity.Editor
|
|
|
556
622
|
|
|
557
623
|
for (var i = 0; i < _removedEntityKeys.Count; i++)
|
|
558
624
|
{
|
|
559
|
-
|
|
560
|
-
var entityNode = _entityNodes[key];
|
|
561
|
-
if (entityNode.Reference.IsRuntimeOnly)
|
|
562
|
-
{
|
|
563
|
-
HierarchySelection.ClearIfSelected(
|
|
564
|
-
entityNode.Reference.RuntimeWorld,
|
|
565
|
-
entityNode.Reference.RuntimeEntity);
|
|
566
|
-
}
|
|
567
|
-
else
|
|
568
|
-
{
|
|
569
|
-
HierarchySelection.ClearIfSelected(this, entityNode.Reference.Authoring, entityNode.Reference.StableId);
|
|
570
|
-
}
|
|
571
|
-
if (Hierarchy.Exists(entityNode.Node))
|
|
572
|
-
{
|
|
573
|
-
CommandList.Remove(entityNode.Node);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
SelectionProxyBridge.Remove(this, entityNode.Node);
|
|
577
|
-
_nodes.Remove(entityNode.Node);
|
|
578
|
-
_entityNodes.Remove(key);
|
|
579
|
-
changed = true;
|
|
625
|
+
changed |= RemoveEntityNode(_removedEntityKeys[i]);
|
|
580
626
|
}
|
|
581
627
|
|
|
582
628
|
if (CommandList.Size > 0)
|
|
@@ -597,33 +643,103 @@ namespace Delta.ECS.Unity.Editor
|
|
|
597
643
|
HierarchyNode sceneNode,
|
|
598
644
|
SceneWorld runtimeWorld)
|
|
599
645
|
{
|
|
600
|
-
|
|
646
|
+
if (!_runtimeStates.TryGetValue(authoring, out RuntimeHierarchyState state)
|
|
647
|
+
|| !ReferenceEquals(state.World, runtimeWorld))
|
|
648
|
+
{
|
|
649
|
+
state?.Dispose();
|
|
650
|
+
state = new RuntimeHierarchyState(runtimeWorld);
|
|
651
|
+
_runtimeStates[authoring] = state;
|
|
652
|
+
}
|
|
653
|
+
state.PrepareChanges();
|
|
654
|
+
return state.Synchronize(this, authoring, sceneNode, out _);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
private bool RefreshRuntimeChanges()
|
|
658
|
+
{
|
|
659
|
+
if (!Hierarchy.IsCreated)
|
|
660
|
+
{
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
601
663
|
bool changed = false;
|
|
602
|
-
|
|
664
|
+
bool authoredChanged = false;
|
|
665
|
+
foreach (var pair in _runtimeStates)
|
|
603
666
|
{
|
|
604
|
-
|
|
605
|
-
if (
|
|
667
|
+
RuntimeHierarchyState state = pair.Value;
|
|
668
|
+
if (!state.World.IsBound || !state.PrepareChanges())
|
|
606
669
|
{
|
|
607
|
-
|
|
670
|
+
continue;
|
|
608
671
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
if (_entityNodes.ContainsKey(key))
|
|
672
|
+
var sceneHandler = Hierarchy.GetNodeTypeHandler<HierarchySceneHandler>();
|
|
673
|
+
HierarchyNode sceneNode = sceneHandler.GetOrCreateNode(pair.Key.gameObject.scene);
|
|
674
|
+
if (sceneNode != HierarchyNode.Null)
|
|
613
675
|
{
|
|
614
|
-
|
|
676
|
+
changed |= state.Synchronize(this, pair.Key, sceneNode, out bool dirtyAuthoring);
|
|
677
|
+
authoredChanged |= dirtyAuthoring;
|
|
615
678
|
}
|
|
679
|
+
}
|
|
680
|
+
if (authoredChanged)
|
|
681
|
+
{
|
|
682
|
+
return Reconcile(changed);
|
|
683
|
+
}
|
|
684
|
+
if (!changed)
|
|
685
|
+
{
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
if (CommandList.Size > 0)
|
|
689
|
+
{
|
|
690
|
+
CommandList.Execute();
|
|
691
|
+
}
|
|
692
|
+
Hierarchy.Update();
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
616
695
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
696
|
+
private bool SynchronizeRuntimeEntity(SceneAuthoring authoring, HierarchyNode sceneNode,
|
|
697
|
+
SceneWorld runtimeWorld, Entity entity)
|
|
698
|
+
{
|
|
699
|
+
var key = new EntityKey(runtimeWorld, entity);
|
|
700
|
+
if (!runtimeWorld.World.IsAlive(entity)
|
|
701
|
+
|| runtimeWorld.HasRuntimeView(entity)
|
|
702
|
+
|| runtimeWorld.World.Has(entity, runtimeWorld.HiddenEntityHierarchyComponentId))
|
|
703
|
+
{
|
|
704
|
+
_activeEntityKeys.Remove(key);
|
|
705
|
+
return RemoveEntityNode(key);
|
|
706
|
+
}
|
|
707
|
+
_activeEntityKeys.Add(key);
|
|
708
|
+
if (_entityNodes.TryGetValue(key, out _))
|
|
709
|
+
{
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
string name = $"ECS Entity {entity.Index}:{entity.Generation}";
|
|
713
|
+
var reference = new NodeReference(authoring, null, runtimeWorld, entity);
|
|
714
|
+
CommandList.Add(sceneNode, out HierarchyNode node);
|
|
715
|
+
CommandList.SetName(node, name);
|
|
716
|
+
_entityNodes.Add(key, new EntityNode(node, reference, name));
|
|
717
|
+
_nodes.Add(node, reference);
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
625
720
|
|
|
626
|
-
|
|
721
|
+
private bool RemoveEntityNode(EntityKey key)
|
|
722
|
+
{
|
|
723
|
+
if (!_entityNodes.Remove(key, out EntityNode entityNode))
|
|
724
|
+
{
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
NodeReference reference = entityNode.Reference;
|
|
728
|
+
if (reference.IsRuntimeOnly)
|
|
729
|
+
{
|
|
730
|
+
HierarchySelection.ClearIfSelected(reference.RuntimeWorld, reference.RuntimeEntity);
|
|
731
|
+
}
|
|
732
|
+
else
|
|
733
|
+
{
|
|
734
|
+
HierarchySelection.ClearIfSelected(this, reference.Authoring, reference.StableId);
|
|
735
|
+
}
|
|
736
|
+
if (Hierarchy.Exists(entityNode.Node))
|
|
737
|
+
{
|
|
738
|
+
CommandList.Remove(entityNode.Node);
|
|
739
|
+
}
|
|
740
|
+
SelectionProxyBridge.Remove(this, entityNode.Node);
|
|
741
|
+
_nodes.Remove(entityNode.Node);
|
|
742
|
+
return true;
|
|
627
743
|
}
|
|
628
744
|
|
|
629
745
|
private void SynchronizeSceneSubscriptions(SceneAuthoring[] authorings)
|
|
@@ -669,6 +785,10 @@ namespace Delta.ECS.Unity.Editor
|
|
|
669
785
|
authoring.SceneData.Changed -= _sceneDataSubscriptions[authoring];
|
|
670
786
|
}
|
|
671
787
|
_sceneDataSubscriptions.Remove(authoring);
|
|
788
|
+
if (_runtimeStates.Remove(authoring, out RuntimeHierarchyState state))
|
|
789
|
+
{
|
|
790
|
+
state.Dispose();
|
|
791
|
+
}
|
|
672
792
|
}
|
|
673
793
|
|
|
674
794
|
_removedAuthorings.Clear();
|
|
@@ -869,6 +989,64 @@ namespace Delta.ECS.Unity.Editor
|
|
|
869
989
|
}
|
|
870
990
|
}
|
|
871
991
|
|
|
992
|
+
private sealed class RuntimeHierarchyState : IDisposable
|
|
993
|
+
{
|
|
994
|
+
private readonly HashSet<Entity> _dirtyEntities = new();
|
|
995
|
+
private readonly ComponentChangeOperation<HideFromEntityHierarchy> _hiddenChanges;
|
|
996
|
+
internal SceneWorld World { get; }
|
|
997
|
+
|
|
998
|
+
internal RuntimeHierarchyState(SceneWorld world)
|
|
999
|
+
{
|
|
1000
|
+
World = world;
|
|
1001
|
+
world.HierarchyEntitiesChanged += Queue;
|
|
1002
|
+
_hiddenChanges = world.World.ForEachChange<HideFromEntityHierarchy>(
|
|
1003
|
+
world.HiddenEntityHierarchyComponentId, OnHiddenChanged);
|
|
1004
|
+
// One snapshot, without tag-slot cache or repeated per-refresh operations.
|
|
1005
|
+
Query query = world.World.WhereNone(ReadOnlySpan<ComponentId>.Empty);
|
|
1006
|
+
world.World.ForEachEntity(in query, QueueInitialEntity).Invoke();
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
public void Dispose() => World.HierarchyEntitiesChanged -= Queue;
|
|
1010
|
+
|
|
1011
|
+
internal bool PrepareChanges()
|
|
1012
|
+
{
|
|
1013
|
+
World.RefreshDestroyedViews();
|
|
1014
|
+
_hiddenChanges.Invoke();
|
|
1015
|
+
return _dirtyEntities.Count != 0;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
internal bool Synchronize(HierarchyNodeHandler handler, SceneAuthoring authoring,
|
|
1019
|
+
HierarchyNode sceneNode, out bool authoredChanged)
|
|
1020
|
+
{
|
|
1021
|
+
bool changed = false;
|
|
1022
|
+
authoredChanged = false;
|
|
1023
|
+
foreach (Entity entity in _dirtyEntities)
|
|
1024
|
+
{
|
|
1025
|
+
if (World.TryGetStableId(entity, out _))
|
|
1026
|
+
{
|
|
1027
|
+
authoredChanged = true;
|
|
1028
|
+
}
|
|
1029
|
+
else
|
|
1030
|
+
{
|
|
1031
|
+
changed |= handler.SynchronizeRuntimeEntity(authoring, sceneNode, World, entity);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
_dirtyEntities.Clear();
|
|
1035
|
+
return changed;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
private void QueueInitialEntity(EntityRef entity) => _dirtyEntities.Add(entity);
|
|
1039
|
+
private void OnHiddenChanged(Entity entity, ChangeKind kind, ref HideFromEntityHierarchy value)
|
|
1040
|
+
=> _dirtyEntities.Add(entity);
|
|
1041
|
+
private void Queue(ReadOnlySpan<Entity> entities)
|
|
1042
|
+
{
|
|
1043
|
+
foreach (Entity entity in entities)
|
|
1044
|
+
{
|
|
1045
|
+
_dirtyEntities.Add(entity);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
872
1050
|
private struct BlockedDrag
|
|
873
1051
|
{
|
|
874
1052
|
public readonly int PointerId;
|
package/README.md
CHANGED
|
@@ -24,6 +24,8 @@ namespace Delta.ECS.Unity
|
|
|
24
24
|
[NonSerialized] private Dictionary<string, GameObject> _viewsByStableId;
|
|
25
25
|
[NonSerialized] private Dictionary<UnityEngine.EntityId, string> _stableIdsByView;
|
|
26
26
|
|
|
27
|
+
internal static event Action HierarchyChanged;
|
|
28
|
+
|
|
27
29
|
internal SceneData SceneData => _sceneData ??= new SceneData();
|
|
28
30
|
|
|
29
31
|
internal static SceneAuthoring FindForScene(Scene scene)
|
|
@@ -120,15 +122,36 @@ namespace Delta.ECS.Unity
|
|
|
120
122
|
|
|
121
123
|
InvalidateBindingIndex();
|
|
122
124
|
EnsureBindingIndex();
|
|
125
|
+
HierarchyChanged?.Invoke();
|
|
123
126
|
return true;
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
internal bool RemoveView(string stableId) => SetView(stableId, null);
|
|
127
130
|
|
|
131
|
+
internal void CollectDestroyedViews(List<string> stableIds)
|
|
132
|
+
{
|
|
133
|
+
EnsureBindingIndex();
|
|
134
|
+
foreach (var pair in _viewsByStableId)
|
|
135
|
+
{
|
|
136
|
+
if (pair.Value == null)
|
|
137
|
+
{
|
|
138
|
+
stableIds.Add(pair.Key);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
foreach (string stableId in stableIds)
|
|
142
|
+
{
|
|
143
|
+
if (_viewsByStableId.Remove(stableId, out GameObject view) && view is not null)
|
|
144
|
+
{
|
|
145
|
+
_stableIdsByView.Remove(view.GetEntityId());
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
128
150
|
private void OnValidate()
|
|
129
151
|
{
|
|
130
152
|
SceneData.EnsureStableIds();
|
|
131
153
|
InvalidateBindingIndex();
|
|
154
|
+
HierarchyChanged?.Invoke();
|
|
132
155
|
}
|
|
133
156
|
|
|
134
157
|
private void OnEnable()
|
|
@@ -136,6 +159,7 @@ namespace Delta.ECS.Unity
|
|
|
136
159
|
SceneData.Changed -= OnSceneDataChanged;
|
|
137
160
|
SceneData.Changed += OnSceneDataChanged;
|
|
138
161
|
InvalidateBindingIndex();
|
|
162
|
+
HierarchyChanged?.Invoke();
|
|
139
163
|
}
|
|
140
164
|
|
|
141
165
|
private void OnDisable()
|
|
@@ -144,6 +168,7 @@ namespace Delta.ECS.Unity
|
|
|
144
168
|
{
|
|
145
169
|
_sceneData.Changed -= OnSceneDataChanged;
|
|
146
170
|
}
|
|
171
|
+
HierarchyChanged?.Invoke();
|
|
147
172
|
}
|
|
148
173
|
|
|
149
174
|
private void OnSceneDataChanged(SceneDataChange change)
|
package/Runtime/SceneWorld.cs
CHANGED
|
@@ -28,6 +28,9 @@ namespace Delta.ECS.Unity
|
|
|
28
28
|
private readonly SceneAuthoring _authoring;
|
|
29
29
|
private readonly bool _ownsWorld;
|
|
30
30
|
private readonly ComponentId _hiddenEntityHierarchyComponentId;
|
|
31
|
+
private readonly List<Entity> _destroyedViews = new();
|
|
32
|
+
private readonly List<string> _destroyedAuthoredViews = new();
|
|
33
|
+
private RuntimeEntityChangeHandler _hierarchyEntitiesChanged;
|
|
31
34
|
private bool _disposed;
|
|
32
35
|
|
|
33
36
|
public World World => _world;
|
|
@@ -41,6 +44,26 @@ namespace Delta.ECS.Unity
|
|
|
41
44
|
return entities;
|
|
42
45
|
}
|
|
43
46
|
}
|
|
47
|
+
internal static event Action LiveWorldsChanged;
|
|
48
|
+
internal event RuntimeEntityChangeHandler HierarchyEntitiesChanged
|
|
49
|
+
{
|
|
50
|
+
add
|
|
51
|
+
{
|
|
52
|
+
if (_hierarchyEntitiesChanged == null)
|
|
53
|
+
{
|
|
54
|
+
_world.EntityMembershipChanged += OnEntityMembershipChanged;
|
|
55
|
+
}
|
|
56
|
+
_hierarchyEntitiesChanged += value;
|
|
57
|
+
}
|
|
58
|
+
remove
|
|
59
|
+
{
|
|
60
|
+
_hierarchyEntitiesChanged -= value;
|
|
61
|
+
if (_hierarchyEntitiesChanged == null)
|
|
62
|
+
{
|
|
63
|
+
_world.EntityMembershipChanged -= OnEntityMembershipChanged;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
44
67
|
internal ComponentId HiddenEntityHierarchyComponentId => _hiddenEntityHierarchyComponentId;
|
|
45
68
|
internal SceneData SceneData => _sceneData;
|
|
46
69
|
internal Scene UnityScene => _authoring != null ? _authoring.gameObject.scene : default;
|
|
@@ -60,6 +83,7 @@ namespace Delta.ECS.Unity
|
|
|
60
83
|
if (registerLiveWorld)
|
|
61
84
|
{
|
|
62
85
|
LiveWorlds.Add(this);
|
|
86
|
+
LiveWorldsChanged?.Invoke();
|
|
63
87
|
}
|
|
64
88
|
}
|
|
65
89
|
|
|
@@ -91,24 +115,35 @@ namespace Delta.ECS.Unity
|
|
|
91
115
|
UnityEngine.EntityId viewId = view.GetEntityId();
|
|
92
116
|
if (_entitiesByView.TryGetValue(viewId, out Entity previousEntity))
|
|
93
117
|
{
|
|
118
|
+
if (previousEntity == entity)
|
|
119
|
+
{
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
94
122
|
_viewsByEntity.Remove(previousEntity);
|
|
123
|
+
NotifyHierarchyEntityChanged(previousEntity);
|
|
95
124
|
}
|
|
96
125
|
|
|
97
|
-
if (_viewsByEntity.TryGetValue(entity, out GameObject previousView) && previousView
|
|
126
|
+
if (_viewsByEntity.TryGetValue(entity, out GameObject previousView) && previousView is not null)
|
|
98
127
|
{
|
|
99
128
|
_entitiesByView.Remove(previousView.GetEntityId());
|
|
100
129
|
}
|
|
101
130
|
|
|
102
131
|
_viewsByEntity[entity] = view;
|
|
103
132
|
_entitiesByView[viewId] = entity;
|
|
133
|
+
NotifyHierarchyEntityChanged(entity);
|
|
104
134
|
return true;
|
|
105
135
|
}
|
|
106
136
|
|
|
107
137
|
public void UnbindView(Entity entity)
|
|
108
138
|
{
|
|
109
|
-
if (_viewsByEntity.Remove(entity, out GameObject view)
|
|
139
|
+
if (_viewsByEntity.Remove(entity, out GameObject view))
|
|
110
140
|
{
|
|
111
|
-
|
|
141
|
+
// The managed wrapper retains its ID even after Unity destroys the native object.
|
|
142
|
+
if (view is not null)
|
|
143
|
+
{
|
|
144
|
+
_entitiesByView.Remove(view.GetEntityId());
|
|
145
|
+
}
|
|
146
|
+
NotifyHierarchyEntityChanged(entity);
|
|
112
147
|
}
|
|
113
148
|
}
|
|
114
149
|
|
|
@@ -143,7 +178,12 @@ namespace Delta.ECS.Unity
|
|
|
143
178
|
}
|
|
144
179
|
|
|
145
180
|
_disposed = true;
|
|
146
|
-
|
|
181
|
+
_world.EntityMembershipChanged -= OnEntityMembershipChanged;
|
|
182
|
+
if (LiveWorlds.Remove(this))
|
|
183
|
+
{
|
|
184
|
+
LiveWorldsChanged?.Invoke();
|
|
185
|
+
}
|
|
186
|
+
_hierarchyEntitiesChanged = null;
|
|
147
187
|
DestroyCurrentAuthoringEntities();
|
|
148
188
|
_entitiesByStableId.Clear();
|
|
149
189
|
_stableIdsByEntity.Clear();
|
|
@@ -159,14 +199,13 @@ namespace Delta.ECS.Unity
|
|
|
159
199
|
/// <summary>Resolves the runtime view first, then the scene-authored stable-ID binding.</summary>
|
|
160
200
|
public bool TryGetView(Entity entity, [NotNullWhen(true)] out GameObject view)
|
|
161
201
|
{
|
|
162
|
-
if (_viewsByEntity.TryGetValue(entity, out view)
|
|
202
|
+
if (_viewsByEntity.TryGetValue(entity, out view))
|
|
163
203
|
{
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
_viewsByEntity.Remove(entity);
|
|
204
|
+
if (view != null)
|
|
205
|
+
{
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
UnbindView(entity);
|
|
170
209
|
}
|
|
171
210
|
|
|
172
211
|
return _authoring != null
|
|
@@ -262,6 +301,36 @@ namespace Delta.ECS.Unity
|
|
|
262
301
|
}
|
|
263
302
|
}
|
|
264
303
|
|
|
304
|
+
internal bool HasRuntimeView(Entity entity) => _viewsByEntity.ContainsKey(entity);
|
|
305
|
+
|
|
306
|
+
internal void RefreshDestroyedViews()
|
|
307
|
+
{
|
|
308
|
+
foreach (var pair in _viewsByEntity)
|
|
309
|
+
{
|
|
310
|
+
if (pair.Value == null || !_world.IsAlive(pair.Key))
|
|
311
|
+
{
|
|
312
|
+
_destroyedViews.Add(pair.Key);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
foreach (Entity entity in _destroyedViews)
|
|
316
|
+
{
|
|
317
|
+
UnbindView(entity);
|
|
318
|
+
}
|
|
319
|
+
_destroyedViews.Clear();
|
|
320
|
+
if (_authoring != null)
|
|
321
|
+
{
|
|
322
|
+
_authoring.CollectDestroyedViews(_destroyedAuthoredViews);
|
|
323
|
+
foreach (string stableId in _destroyedAuthoredViews)
|
|
324
|
+
{
|
|
325
|
+
if (_entitiesByStableId.TryGetValue(stableId, out Entity entity))
|
|
326
|
+
{
|
|
327
|
+
NotifyHierarchyEntityChanged(entity);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
_destroyedAuthoredViews.Clear();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
265
334
|
internal void RebuildAuthoringEntities()
|
|
266
335
|
{
|
|
267
336
|
ThrowIfDisposed();
|
|
@@ -521,6 +590,15 @@ namespace Delta.ECS.Unity
|
|
|
521
590
|
return new World(layouts, initialEntityCapacity);
|
|
522
591
|
}
|
|
523
592
|
|
|
593
|
+
private void OnEntityMembershipChanged(ReadOnlySpan<Entity> entities)
|
|
594
|
+
=> _hierarchyEntitiesChanged?.Invoke(entities);
|
|
595
|
+
|
|
596
|
+
private void NotifyHierarchyEntityChanged(Entity entity)
|
|
597
|
+
{
|
|
598
|
+
ReadOnlySpan<Entity> entities = stackalloc Entity[] { entity };
|
|
599
|
+
_hierarchyEntitiesChanged?.Invoke(entities);
|
|
600
|
+
}
|
|
601
|
+
|
|
524
602
|
private void DestroyCurrentAuthoringEntities()
|
|
525
603
|
{
|
|
526
604
|
foreach (Entity entity in _entitiesByStableId.Values)
|
|
@@ -544,6 +622,8 @@ namespace Delta.ECS.Unity
|
|
|
544
622
|
}
|
|
545
623
|
}
|
|
546
624
|
|
|
625
|
+
internal delegate void RuntimeEntityChangeHandler(ReadOnlySpan<Entity> entities);
|
|
626
|
+
|
|
547
627
|
/// <summary>Owns runtime world creation and teardown for loaded Unity scenes.</summary>
|
|
548
628
|
internal static class SceneWorldRuntime
|
|
549
629
|
{
|