com.artromskiy.deltaecs.unity 0.0.64 → 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 +8 -0
- package/Editor/HierarchyNodeHandler.cs +238 -71
- 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
|
@@ -19,25 +19,31 @@ namespace Delta.ECS.Unity.Editor
|
|
|
19
19
|
internal sealed class HierarchyNodeHandler : HierarchyNodeTypeHandler
|
|
20
20
|
{
|
|
21
21
|
private const double RuntimeHierarchyRefreshInterval = 0.5d;
|
|
22
|
-
private static readonly ComponentId[] NoRequiredComponents = Array.Empty<ComponentId>();
|
|
23
22
|
private static readonly SchemaId HiddenEntitySchema = new(ViewComponentSchemaIds.HideFromEntityHierarchy);
|
|
24
23
|
private static double _nextRuntimeHierarchyRefresh;
|
|
25
24
|
private static bool _wasPlaying;
|
|
25
|
+
private static bool _authoringsDirty = true;
|
|
26
|
+
private static SceneAuthoring[] _authorings = Array.Empty<SceneAuthoring>();
|
|
26
27
|
private static readonly HashSet<HierarchyNodeHandler> Instances = new();
|
|
27
28
|
|
|
28
29
|
private readonly Dictionary<EntityKey, EntityNode> _entityNodes = new();
|
|
30
|
+
private readonly Dictionary<SceneAuthoring, RuntimeHierarchyState> _runtimeStates = new();
|
|
29
31
|
private readonly HashSet<EntityKey> _activeEntityKeys = new();
|
|
30
32
|
private readonly List<EntityKey> _removedEntityKeys = new();
|
|
31
33
|
private readonly Dictionary<HierarchyNode, NodeReference> _nodes = new();
|
|
32
34
|
private readonly HashSet<HierarchyView> _boundViews = new();
|
|
33
35
|
private readonly Dictionary<HierarchyView, BlockedDrag> _blockedDrags = new();
|
|
34
36
|
private readonly Dictionary<SceneAuthoring, Action<SceneDataChange>> _sceneDataSubscriptions = new();
|
|
37
|
+
private readonly HashSet<SceneAuthoring> _loadedAuthorings = new();
|
|
38
|
+
private readonly List<SceneAuthoring> _removedAuthorings = new();
|
|
35
39
|
|
|
36
40
|
static HierarchyNodeHandler()
|
|
37
41
|
{
|
|
38
42
|
HierarchyWindow.BindView += BindHierarchyView;
|
|
39
43
|
EditorApplication.delayCall += BindExistingHierarchyViews;
|
|
40
44
|
EditorApplication.update += UpdateRuntimeHierarchy;
|
|
45
|
+
SceneAuthoring.HierarchyChanged += InvalidateAuthorings;
|
|
46
|
+
SceneWorld.LiveWorldsChanged += InvalidateAuthorings;
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
public override string GetNodeTypeName() => nameof(HierarchyNodeHandler);
|
|
@@ -97,7 +103,14 @@ namespace Delta.ECS.Unity.Editor
|
|
|
97
103
|
pair.Key.SceneData.Changed -= pair.Value;
|
|
98
104
|
}
|
|
99
105
|
}
|
|
106
|
+
foreach (RuntimeHierarchyState state in _runtimeStates.Values)
|
|
107
|
+
{
|
|
108
|
+
state.Dispose();
|
|
109
|
+
}
|
|
110
|
+
_runtimeStates.Clear();
|
|
100
111
|
_sceneDataSubscriptions.Clear();
|
|
112
|
+
_loadedAuthorings.Clear();
|
|
113
|
+
_removedAuthorings.Clear();
|
|
101
114
|
_entityNodes.Clear();
|
|
102
115
|
_activeEntityKeys.Clear();
|
|
103
116
|
_removedEntityKeys.Clear();
|
|
@@ -147,6 +160,8 @@ namespace Delta.ECS.Unity.Editor
|
|
|
147
160
|
|
|
148
161
|
internal static void RefreshAll()
|
|
149
162
|
{
|
|
163
|
+
InvalidateAuthorings();
|
|
164
|
+
GetAuthorings();
|
|
150
165
|
var changed = false;
|
|
151
166
|
foreach (var instance in Instances)
|
|
152
167
|
{
|
|
@@ -203,7 +218,22 @@ namespace Delta.ECS.Unity.Editor
|
|
|
203
218
|
}
|
|
204
219
|
|
|
205
220
|
_nextRuntimeHierarchyRefresh = EditorApplication.timeSinceStartup + RuntimeHierarchyRefreshInterval;
|
|
206
|
-
|
|
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
|
+
}
|
|
207
237
|
return;
|
|
208
238
|
}
|
|
209
239
|
|
|
@@ -214,6 +244,18 @@ namespace Delta.ECS.Unity.Editor
|
|
|
214
244
|
}
|
|
215
245
|
}
|
|
216
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
|
+
|
|
217
259
|
private static void BindHierarchyView(HierarchyWindow window, HierarchyView view)
|
|
218
260
|
{
|
|
219
261
|
if (view == null)
|
|
@@ -429,19 +471,46 @@ namespace Delta.ECS.Unity.Editor
|
|
|
429
471
|
}
|
|
430
472
|
}
|
|
431
473
|
|
|
432
|
-
private bool Reconcile()
|
|
474
|
+
private bool Reconcile(bool changed = false)
|
|
433
475
|
{
|
|
434
476
|
if (!Hierarchy.IsCreated)
|
|
435
477
|
{
|
|
436
478
|
return false;
|
|
437
479
|
}
|
|
438
480
|
|
|
439
|
-
var changed = false;
|
|
440
481
|
_activeEntityKeys.Clear();
|
|
441
482
|
|
|
442
483
|
var sceneHandler = Hierarchy.GetNodeTypeHandler<HierarchySceneHandler>();
|
|
443
|
-
var authoringsInScenes =
|
|
484
|
+
var authoringsInScenes = GetAuthorings();
|
|
444
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
|
+
}
|
|
445
514
|
for (var i = 0; i < authoringsInScenes.Length; i++)
|
|
446
515
|
{
|
|
447
516
|
var authoringHost = authoringsInScenes[i];
|
|
@@ -461,6 +530,7 @@ namespace Delta.ECS.Unity.Editor
|
|
|
461
530
|
{
|
|
462
531
|
SceneWorld.TryFindLiveWorld(authoringHost.SceneData, out runtimeWorld);
|
|
463
532
|
}
|
|
533
|
+
runtimeWorld?.RefreshDestroyedViews();
|
|
464
534
|
|
|
465
535
|
var entities = authoringHost.SceneData.Entities;
|
|
466
536
|
for (var entityIndex = 0; entityIndex < entities.Count; entityIndex++)
|
|
@@ -481,8 +551,7 @@ namespace Delta.ECS.Unity.Editor
|
|
|
481
551
|
: default;
|
|
482
552
|
bool hasView = authoringHost.TryGetView(authoring.StableId, out _)
|
|
483
553
|
|| (runtimeWorld != null
|
|
484
|
-
&& runtimeWorld.
|
|
485
|
-
&& runtimeWorld.TryGetView(runtimeEntity, out _));
|
|
554
|
+
&& runtimeWorld.HasRuntimeView(runtimeEntity));
|
|
486
555
|
if (hasView || IsHiddenFromHierarchy(authoring, runtimeWorld, runtimeEntity))
|
|
487
556
|
{
|
|
488
557
|
continue;
|
|
@@ -553,27 +622,7 @@ namespace Delta.ECS.Unity.Editor
|
|
|
553
622
|
|
|
554
623
|
for (var i = 0; i < _removedEntityKeys.Count; i++)
|
|
555
624
|
{
|
|
556
|
-
|
|
557
|
-
var entityNode = _entityNodes[key];
|
|
558
|
-
if (entityNode.Reference.IsRuntimeOnly)
|
|
559
|
-
{
|
|
560
|
-
HierarchySelection.ClearIfSelected(
|
|
561
|
-
entityNode.Reference.RuntimeWorld,
|
|
562
|
-
entityNode.Reference.RuntimeEntity);
|
|
563
|
-
}
|
|
564
|
-
else
|
|
565
|
-
{
|
|
566
|
-
HierarchySelection.ClearIfSelected(this, entityNode.Reference.Authoring, entityNode.Reference.StableId);
|
|
567
|
-
}
|
|
568
|
-
if (Hierarchy.Exists(entityNode.Node))
|
|
569
|
-
{
|
|
570
|
-
CommandList.Remove(entityNode.Node);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
SelectionProxyBridge.Remove(this, entityNode.Node);
|
|
574
|
-
_nodes.Remove(entityNode.Node);
|
|
575
|
-
_entityNodes.Remove(key);
|
|
576
|
-
changed = true;
|
|
625
|
+
changed |= RemoveEntityNode(_removedEntityKeys[i]);
|
|
577
626
|
}
|
|
578
627
|
|
|
579
628
|
if (CommandList.Size > 0)
|
|
@@ -594,54 +643,108 @@ namespace Delta.ECS.Unity.Editor
|
|
|
594
643
|
HierarchyNode sceneNode,
|
|
595
644
|
SceneWorld runtimeWorld)
|
|
596
645
|
{
|
|
597
|
-
|
|
598
|
-
.
|
|
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
|
+
}
|
|
599
663
|
bool changed = false;
|
|
600
|
-
|
|
664
|
+
bool authoredChanged = false;
|
|
665
|
+
foreach (var pair in _runtimeStates)
|
|
601
666
|
{
|
|
602
|
-
|
|
667
|
+
RuntimeHierarchyState state = pair.Value;
|
|
668
|
+
if (!state.World.IsBound || !state.PrepareChanges())
|
|
603
669
|
{
|
|
604
|
-
|
|
670
|
+
continue;
|
|
605
671
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
string name = $"ECS Entity {entity.Index}:{entity.Generation}";
|
|
610
|
-
var reference = new NodeReference(authoring, null, runtimeWorld, entity);
|
|
611
|
-
if (_entityNodes.TryGetValue(key, out EntityNode entityNode))
|
|
672
|
+
var sceneHandler = Hierarchy.GetNodeTypeHandler<HierarchySceneHandler>();
|
|
673
|
+
HierarchyNode sceneNode = sceneHandler.GetOrCreateNode(pair.Key.gameObject.scene);
|
|
674
|
+
if (sceneNode != HierarchyNode.Null)
|
|
612
675
|
{
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
{
|
|
616
|
-
entityNode.Reference = reference;
|
|
617
|
-
changed = true;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
if (!string.Equals(entityNode.Name, name, StringComparison.Ordinal))
|
|
621
|
-
{
|
|
622
|
-
CommandList.SetName(entityNode.Node, name);
|
|
623
|
-
entityNode.Name = name;
|
|
624
|
-
changed = true;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
_entityNodes[key] = entityNode;
|
|
628
|
-
_nodes[entityNode.Node] = reference;
|
|
629
|
-
return;
|
|
676
|
+
changed |= state.Synchronize(this, pair.Key, sceneNode, out bool dirtyAuthoring);
|
|
677
|
+
authoredChanged |= dirtyAuthoring;
|
|
630
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
|
+
}
|
|
631
695
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
+
}
|
|
638
720
|
|
|
639
|
-
|
|
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;
|
|
640
743
|
}
|
|
641
744
|
|
|
642
745
|
private void SynchronizeSceneSubscriptions(SceneAuthoring[] authorings)
|
|
643
746
|
{
|
|
644
|
-
|
|
747
|
+
_loadedAuthorings.Clear();
|
|
645
748
|
for (var i = 0; i < authorings.Length; i++)
|
|
646
749
|
{
|
|
647
750
|
var authoring = authorings[i];
|
|
@@ -650,7 +753,7 @@ namespace Delta.ECS.Unity.Editor
|
|
|
650
753
|
continue;
|
|
651
754
|
}
|
|
652
755
|
|
|
653
|
-
|
|
756
|
+
_loadedAuthorings.Add(authoring);
|
|
654
757
|
if (!_sceneDataSubscriptions.ContainsKey(authoring))
|
|
655
758
|
{
|
|
656
759
|
var capturedAuthoring = authoring;
|
|
@@ -660,29 +763,35 @@ namespace Delta.ECS.Unity.Editor
|
|
|
660
763
|
}
|
|
661
764
|
}
|
|
662
765
|
|
|
663
|
-
if (_sceneDataSubscriptions.Count ==
|
|
766
|
+
if (_sceneDataSubscriptions.Count == _loadedAuthorings.Count)
|
|
664
767
|
{
|
|
665
768
|
return;
|
|
666
769
|
}
|
|
667
770
|
|
|
668
|
-
|
|
771
|
+
_removedAuthorings.Clear();
|
|
669
772
|
foreach (var authoring in _sceneDataSubscriptions.Keys)
|
|
670
773
|
{
|
|
671
|
-
if (!
|
|
774
|
+
if (!_loadedAuthorings.Contains(authoring))
|
|
672
775
|
{
|
|
673
|
-
|
|
776
|
+
_removedAuthorings.Add(authoring);
|
|
674
777
|
}
|
|
675
778
|
}
|
|
676
779
|
|
|
677
|
-
for (var i = 0; i <
|
|
780
|
+
for (var i = 0; i < _removedAuthorings.Count; i++)
|
|
678
781
|
{
|
|
679
|
-
var authoring =
|
|
782
|
+
var authoring = _removedAuthorings[i];
|
|
680
783
|
if (authoring != null)
|
|
681
784
|
{
|
|
682
785
|
authoring.SceneData.Changed -= _sceneDataSubscriptions[authoring];
|
|
683
786
|
}
|
|
684
787
|
_sceneDataSubscriptions.Remove(authoring);
|
|
788
|
+
if (_runtimeStates.Remove(authoring, out RuntimeHierarchyState state))
|
|
789
|
+
{
|
|
790
|
+
state.Dispose();
|
|
791
|
+
}
|
|
685
792
|
}
|
|
793
|
+
|
|
794
|
+
_removedAuthorings.Clear();
|
|
686
795
|
}
|
|
687
796
|
|
|
688
797
|
private void OnSceneDataChanged(SceneAuthoring authoring, SceneDataChange change)
|
|
@@ -880,6 +989,64 @@ namespace Delta.ECS.Unity.Editor
|
|
|
880
989
|
}
|
|
881
990
|
}
|
|
882
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
|
+
|
|
883
1050
|
private struct BlockedDrag
|
|
884
1051
|
{
|
|
885
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
|
{
|