com.artromskiy.deltaecs.unity 0.0.37

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/Editor/BoundEntityRootEditor.cs +288 -0
  3. package/Editor/BoundEntityRootEditor.cs.meta +2 -0
  4. package/Editor/ComponentDropdown.cs +73 -0
  5. package/Editor/ComponentDropdown.cs.meta +2 -0
  6. package/Editor/ComponentListController.cs +26 -0
  7. package/Editor/ComponentListController.cs.meta +2 -0
  8. package/Editor/DeltaECS.Unity.Editor.asmdef +18 -0
  9. package/Editor/DeltaECS.Unity.Editor.asmdef.meta +7 -0
  10. package/Editor/EditorLifecycle.cs +143 -0
  11. package/Editor/EditorLifecycle.cs.meta +2 -0
  12. package/Editor/EntityInspectorTarget.cs +26 -0
  13. package/Editor/EntityInspectorTarget.cs.meta +3 -0
  14. package/Editor/HierarchyMenu.cs +85 -0
  15. package/Editor/HierarchyMenu.cs.meta +2 -0
  16. package/Editor/HierarchyNodeHandler.cs +950 -0
  17. package/Editor/HierarchyNodeHandler.cs.meta +2 -0
  18. package/Editor/HierarchySelection.cs +171 -0
  19. package/Editor/HierarchySelection.cs.meta +3 -0
  20. package/Editor/InspectorState.cs +220 -0
  21. package/Editor/InspectorState.cs.meta +2 -0
  22. package/Editor/Resources/Inspector/BoundEntityRootEditor.uss +28 -0
  23. package/Editor/Resources/Inspector/BoundEntityRootEditor.uss.meta +2 -0
  24. package/Editor/Resources/Inspector/SceneAuthoringEditor.uss +282 -0
  25. package/Editor/Resources/Inspector/SceneAuthoringEditor.uss.meta +12 -0
  26. package/Editor/Resources/Inspector/SceneAuthoringEditor.uxml +8 -0
  27. package/Editor/Resources/Inspector/SceneAuthoringEditor.uxml.meta +10 -0
  28. package/Editor/Resources/Inspector.meta +8 -0
  29. package/Editor/Resources.meta +8 -0
  30. package/Editor/SceneEntityInspectorView.cs +1103 -0
  31. package/Editor/SceneEntityInspectorView.cs.meta +2 -0
  32. package/Editor/SelectionProxy.cs +19 -0
  33. package/Editor/SelectionProxy.cs.meta +2 -0
  34. package/Editor/SelectionProxyBridge.cs +64 -0
  35. package/Editor/SelectionProxyBridge.cs.meta +2 -0
  36. package/Editor/SelectionProxyEditor.cs +33 -0
  37. package/Editor/SelectionProxyEditor.cs.meta +2 -0
  38. package/LICENSE.md +21 -0
  39. package/LICENSE.md.meta +7 -0
  40. package/README.md +66 -0
  41. package/README.md.meta +2 -0
  42. package/Runtime/AssemblyInfo.cs +3 -0
  43. package/Runtime/AssemblyInfo.cs.meta +2 -0
  44. package/Runtime/ComponentData.cs +120 -0
  45. package/Runtime/ComponentData.cs.meta +2 -0
  46. package/Runtime/DeltaECS.Unity.asmdef +14 -0
  47. package/Runtime/DeltaECS.Unity.asmdef.meta +7 -0
  48. package/Runtime/Editor.meta +8 -0
  49. package/Runtime/EntityAuthoring.cs +47 -0
  50. package/Runtime/EntityAuthoring.cs.meta +2 -0
  51. package/Runtime/EntityReference.cs +21 -0
  52. package/Runtime/EntityReference.cs.meta +2 -0
  53. package/Runtime/SceneAuthoring.cs +202 -0
  54. package/Runtime/SceneAuthoring.cs.meta +2 -0
  55. package/Runtime/SceneData.cs +120 -0
  56. package/Runtime/SceneData.cs.meta +2 -0
  57. package/Runtime/SceneWorld.cs +618 -0
  58. package/Runtime/SceneWorld.cs.meta +2 -0
  59. package/Runtime/ThrowHelpers.cs +44 -0
  60. package/Runtime/ThrowHelpers.cs.meta +2 -0
  61. package/package.json +31 -0
@@ -0,0 +1,950 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using Delta.ECS;
4
+ using UnityEditor;
5
+ using UnityEditor.SceneManagement;
6
+ using Unity.Hierarchy;
7
+ using Unity.Hierarchy.Editor;
8
+ using UnityEngine;
9
+ using UnityEngine.SceneManagement;
10
+ using UnityEngine.UIElements;
11
+
12
+ namespace Delta.ECS.Unity.Editor
13
+ {
14
+ [InitializeOnLoad]
15
+ internal sealed class HierarchyNodeHandler : HierarchyNodeTypeHandler
16
+ {
17
+ private static readonly ComponentId[] NoRequiredComponents = Array.Empty<ComponentId>();
18
+ private const double RuntimeHierarchyRefreshInterval = 0.5d;
19
+ private static double _nextRuntimeHierarchyRefresh;
20
+ private static bool _wasPlaying;
21
+
22
+ private readonly Dictionary<EntityKey, EntityNode> _entityNodes = new();
23
+ private readonly HashSet<EntityKey> _activeEntityKeys = new();
24
+ private readonly List<EntityKey> _removedEntityKeys = new();
25
+ private readonly Dictionary<HierarchyNode, NodeReference> _nodes = new();
26
+ private readonly HashSet<HierarchyView> _boundViews = new();
27
+ private readonly Dictionary<HierarchyView, BlockedDrag> _blockedDrags = new();
28
+ private readonly Dictionary<SceneAuthoring, Action<SceneDataChange>> _sceneDataSubscriptions = new();
29
+ private static readonly HashSet<HierarchyNodeHandler> Instances = new();
30
+
31
+ static HierarchyNodeHandler()
32
+ {
33
+ HierarchyWindow.BindView += BindHierarchyView;
34
+ EditorApplication.delayCall += BindExistingHierarchyViews;
35
+ EditorApplication.update += UpdateRuntimeHierarchy;
36
+ }
37
+
38
+ private static void UpdateRuntimeHierarchy()
39
+ {
40
+ if (EditorApplication.isPlaying)
41
+ {
42
+ _wasPlaying = true;
43
+ if (EditorApplication.timeSinceStartup < _nextRuntimeHierarchyRefresh)
44
+ {
45
+ return;
46
+ }
47
+
48
+ _nextRuntimeHierarchyRefresh = EditorApplication.timeSinceStartup + RuntimeHierarchyRefreshInterval;
49
+ RefreshAll();
50
+ return;
51
+ }
52
+
53
+ if (_wasPlaying && !EditorApplication.isPlayingOrWillChangePlaymode)
54
+ {
55
+ _wasPlaying = false;
56
+ RefreshAll();
57
+ }
58
+ }
59
+
60
+ private static void BindHierarchyView(HierarchyWindow window, HierarchyView view)
61
+ {
62
+ if (view == null)
63
+ {
64
+ return;
65
+ }
66
+
67
+ var handler =
68
+ view.Source.GetOrCreateNodeTypeHandler<HierarchyNodeHandler>();
69
+ handler.BindToView(view);
70
+ }
71
+
72
+ private static void BindExistingHierarchyViews()
73
+ {
74
+ var windows = Resources.FindObjectsOfTypeAll<HierarchyWindow>();
75
+ for (var i = 0; i < windows.Length; i++)
76
+ {
77
+ var window = windows[i];
78
+ if (window != null)
79
+ {
80
+ BindHierarchyView(window, window.View);
81
+ }
82
+ }
83
+ }
84
+
85
+ public override string GetNodeTypeName() => nameof(HierarchyNodeHandler);
86
+
87
+ public override EntityId GetEntityIdFromNode(in HierarchyNode node) =>
88
+ _nodes.TryGetValue(node, out var reference) && reference.IsEntity
89
+ ? SelectionProxyBridge.GetEntityId(this, node,
90
+ new EntityInspectorTarget(reference.Authoring, reference.StableId, reference.RuntimeWorld, reference.RuntimeEntity))
91
+ : EntityId.None;
92
+
93
+ public override HierarchyNode GetNodeFromEntityId(EntityId entityId) =>
94
+ SelectionProxyBridge.GetNode(this, entityId);
95
+
96
+ public override int GetEntityIdsFromNodes(ReadOnlySpan<HierarchyNode> nodes, Span<EntityId> outEntityIds)
97
+ {
98
+ int remaining = 0;
99
+ for (int i = 0; i < nodes.Length; i++)
100
+ {
101
+ if (outEntityIds[i] == EntityId.None)
102
+ outEntityIds[i] = GetEntityIdFromNode(in nodes[i]);
103
+ if (outEntityIds[i] == EntityId.None) remaining++;
104
+ }
105
+ return remaining;
106
+ }
107
+
108
+ public override int GetNodesFromEntityIds(ReadOnlySpan<EntityId> entityIds, Span<HierarchyNode> outNodes)
109
+ {
110
+ int remaining = 0;
111
+ for (int i = 0; i < entityIds.Length; i++)
112
+ {
113
+ if (outNodes[i] == HierarchyNode.Null)
114
+ outNodes[i] = GetNodeFromEntityId(entityIds[i]);
115
+ if (outNodes[i] == HierarchyNode.Null) remaining++;
116
+ }
117
+ return remaining;
118
+ }
119
+
120
+ public override HierarchyNodeFlags GetDefaultNodeFlags(in HierarchyNode node, HierarchyNodeFlags defaultFlags = HierarchyNodeFlags.None) =>
121
+ defaultFlags | HierarchyNodeFlags.Expanded;
122
+
123
+ protected override void Initialize()
124
+ {
125
+ Instances.Add(this);
126
+ Reconcile();
127
+ }
128
+
129
+ protected override void Dispose(bool disposing)
130
+ {
131
+ foreach (var node in _nodes.Keys)
132
+ SelectionProxyBridge.Remove(this, node);
133
+ Instances.Remove(this);
134
+ HierarchySelection.ClearHandler(this);
135
+ foreach (var pair in _sceneDataSubscriptions)
136
+ {
137
+ if (pair.Key != null)
138
+ {
139
+ pair.Key.SceneData.Changed -= pair.Value;
140
+ }
141
+ }
142
+ _sceneDataSubscriptions.Clear();
143
+ _entityNodes.Clear();
144
+ _activeEntityKeys.Clear();
145
+ _removedEntityKeys.Clear();
146
+ _nodes.Clear();
147
+ foreach (var view in _boundViews)
148
+ {
149
+ UnbindDragGuard(view);
150
+ view.FlagsChanged -= OnHierarchyFlagsChanged;
151
+ view.PopulateContextMenu -= PopulateContextMenu;
152
+ HierarchySelection.Clear(view);
153
+ }
154
+ _boundViews.Clear();
155
+
156
+ base.Dispose(disposing);
157
+ }
158
+
159
+ protected override void OnBindView(HierarchyView view)
160
+ {
161
+ if (!_boundViews.Add(view))
162
+ {
163
+ return;
164
+ }
165
+
166
+ BindDragGuard(view);
167
+ view.FlagsChanged += OnHierarchyFlagsChanged;
168
+ view.PopulateContextMenu += PopulateContextMenu;
169
+ }
170
+
171
+ protected override void OnBindItem(HierarchyViewItem item)
172
+ {
173
+ item.Name.RegisterValueChangedCallback(OnEntityNameChanged);
174
+ }
175
+
176
+ protected override void OnUnbindItem(HierarchyViewItem item)
177
+ {
178
+ item.Name.UnregisterValueChangedCallback(OnEntityNameChanged);
179
+ }
180
+
181
+ private void BindToView(HierarchyView view) => OnBindView(view);
182
+
183
+ private void OnEntityNameChanged(ChangeEvent<string> evt)
184
+ {
185
+ if (EditorApplication.isPlayingOrWillChangePlaymode
186
+ || string.Equals(evt.previousValue, evt.newValue))
187
+ {
188
+ return;
189
+ }
190
+
191
+ var element = evt.currentTarget as VisualElement;
192
+ while (element != null && element is not HierarchyViewItem)
193
+ {
194
+ element = element.parent;
195
+ }
196
+
197
+ if (element is not HierarchyViewItem item
198
+ || !_nodes.TryGetValue(item.Node, out var reference)
199
+ || !reference.IsEntity
200
+ || !reference.Authoring.SceneData.TryGetEntity(reference.StableId, out var entity)
201
+ || string.Equals(entity.Name, evt.newValue))
202
+ {
203
+ return;
204
+ }
205
+
206
+ Undo.RecordObject(reference.Authoring, "Rename Delta ECS Entity");
207
+ if (reference.Authoring.SceneData.RenameEntity(reference.StableId, evt.newValue))
208
+ {
209
+ HierarchyMenu.MarkSceneDirty(reference.Authoring);
210
+ }
211
+ }
212
+
213
+ protected override void OnUnbindView(HierarchyView view)
214
+ {
215
+ UnbindDragGuard(view);
216
+ view.FlagsChanged -= OnHierarchyFlagsChanged;
217
+ view.PopulateContextMenu -= PopulateContextMenu;
218
+ _boundViews.Remove(view);
219
+ HierarchySelection.Clear(view);
220
+ }
221
+
222
+ private void BindDragGuard(HierarchyView view)
223
+ {
224
+ view.RegisterCallback<PointerDownEvent>(OnHierarchyPointerDown, TrickleDown.TrickleDown);
225
+ view.RegisterCallback<PointerMoveEvent>(OnHierarchyPointerMove, TrickleDown.TrickleDown);
226
+ view.RegisterCallback<PointerUpEvent>(OnHierarchyPointerUp, TrickleDown.TrickleDown);
227
+ view.RegisterCallback<PointerCancelEvent>(OnHierarchyPointerCancel, TrickleDown.TrickleDown);
228
+ }
229
+
230
+ private void UnbindDragGuard(HierarchyView view)
231
+ {
232
+ view.UnregisterCallback<PointerDownEvent>(OnHierarchyPointerDown, TrickleDown.TrickleDown);
233
+ view.UnregisterCallback<PointerMoveEvent>(OnHierarchyPointerMove, TrickleDown.TrickleDown);
234
+ view.UnregisterCallback<PointerUpEvent>(OnHierarchyPointerUp, TrickleDown.TrickleDown);
235
+ view.UnregisterCallback<PointerCancelEvent>(OnHierarchyPointerCancel, TrickleDown.TrickleDown);
236
+ _blockedDrags.Remove(view);
237
+ }
238
+
239
+ private void OnHierarchyPointerDown(PointerDownEvent evt)
240
+ {
241
+ if (evt.button != 0 || evt.currentTarget is not HierarchyView view)
242
+ {
243
+ return;
244
+ }
245
+
246
+ var clickedEntity = PointerDownOnEntity(evt.target as VisualElement);
247
+ var extendsSelection = evt.ctrlKey || evt.commandKey || evt.shiftKey;
248
+ if (clickedEntity || (extendsSelection && HasSelectedEntity(view)))
249
+ {
250
+ var position = evt.position;
251
+ _blockedDrags[view] = new BlockedDrag(evt.pointerId, new Vector2(position.x, position.y));
252
+ }
253
+ else
254
+ {
255
+ _blockedDrags.Remove(view);
256
+ }
257
+ }
258
+
259
+ private bool HasSelectedEntity(HierarchyView view)
260
+ {
261
+ foreach (ref readonly var node in view.ViewModel.EnumerateNodesWithFlags(HierarchyNodeFlags.Selected))
262
+ {
263
+ if (_nodes.TryGetValue(node, out var reference) && reference.IsEntity)
264
+ {
265
+ return true;
266
+ }
267
+ }
268
+
269
+ return false;
270
+ }
271
+
272
+ private bool PointerDownOnEntity(VisualElement target)
273
+ {
274
+ for (var element = target; element != null; element = element.parent)
275
+ {
276
+ if (element is HierarchyViewItem item && _nodes.ContainsKey(item.Node))
277
+ {
278
+ return true;
279
+ }
280
+ }
281
+
282
+ return false;
283
+ }
284
+
285
+ private void OnHierarchyPointerMove(PointerMoveEvent evt)
286
+ {
287
+ if (evt.currentTarget is not HierarchyView view
288
+ || !_blockedDrags.TryGetValue(view, out var blockedDrag)
289
+ || evt.pointerId != blockedDrag.PointerId)
290
+ {
291
+ return;
292
+ }
293
+
294
+ var position = evt.position;
295
+ var pointerPosition = new Vector2(position.x, position.y);
296
+ if (!blockedDrag.ThresholdReached && (pointerPosition - blockedDrag.StartPosition).sqrMagnitude < 16f)
297
+ {
298
+ return;
299
+ }
300
+
301
+ blockedDrag.ThresholdReached = true;
302
+ _blockedDrags[view] = blockedDrag;
303
+ evt.StopImmediatePropagation();
304
+ }
305
+
306
+ private void OnHierarchyPointerUp(PointerUpEvent evt)
307
+ {
308
+ ClearBlockedDrag(evt.currentTarget as HierarchyView, evt.pointerId);
309
+ }
310
+
311
+ private void OnHierarchyPointerCancel(PointerCancelEvent evt)
312
+ {
313
+ ClearBlockedDrag(evt.currentTarget as HierarchyView, evt.pointerId);
314
+ }
315
+
316
+ private void ClearBlockedDrag(HierarchyView view, int pointerId)
317
+ {
318
+ if (view != null && _blockedDrags.TryGetValue(view, out var blockedDrag)
319
+ && blockedDrag.PointerId == pointerId)
320
+ {
321
+ _blockedDrags.Remove(view);
322
+ }
323
+ }
324
+
325
+ private struct BlockedDrag
326
+ {
327
+ public readonly int PointerId;
328
+ public readonly Vector2 StartPosition;
329
+ public bool ThresholdReached;
330
+
331
+ public BlockedDrag(int pointerId, Vector2 startPosition)
332
+ {
333
+ PointerId = pointerId;
334
+ StartPosition = startPosition;
335
+ ThresholdReached = false;
336
+ }
337
+ }
338
+
339
+ internal static void RefreshAll()
340
+ {
341
+ var changed = false;
342
+ foreach (var instance in Instances)
343
+ {
344
+ changed |= instance.Reconcile();
345
+ }
346
+
347
+ if (changed)
348
+ {
349
+ EditorApplication.RepaintHierarchyWindow();
350
+ }
351
+ }
352
+
353
+ internal static SceneAuthoring FindAuthoringForScene(Scene scene)
354
+ => SceneAuthoring.FindForScene(scene);
355
+
356
+ private static bool IsHierarchyScene(Scene scene) =>
357
+ scene.IsValid() && scene.isLoaded && !EditorSceneManager.IsPreviewScene(scene);
358
+
359
+ private bool Reconcile()
360
+ {
361
+ if (!Hierarchy.IsCreated)
362
+ {
363
+ return false;
364
+ }
365
+
366
+ var changed = false;
367
+ _activeEntityKeys.Clear();
368
+
369
+ var sceneHandler = Hierarchy.GetNodeTypeHandler<HierarchySceneHandler>();
370
+ var authoringsInScenes = Resources.FindObjectsOfTypeAll<SceneAuthoring>();
371
+ SynchronizeSceneSubscriptions(authoringsInScenes);
372
+ for (var i = 0; i < authoringsInScenes.Length; i++)
373
+ {
374
+ var authoringHost = authoringsInScenes[i];
375
+ if (authoringHost == null || !IsHierarchyScene(authoringHost.gameObject.scene))
376
+ {
377
+ continue;
378
+ }
379
+
380
+ var sceneNode = sceneHandler.GetOrCreateNode(authoringHost.gameObject.scene);
381
+ if (sceneNode == HierarchyNode.Null)
382
+ {
383
+ continue;
384
+ }
385
+
386
+ SceneWorld runtimeWorld = null;
387
+ if (EditorApplication.isPlaying)
388
+ {
389
+ SceneWorld.TryFindLiveWorld(authoringHost.SceneData, out runtimeWorld);
390
+ }
391
+
392
+ var entities = authoringHost.SceneData.Entities;
393
+ for (var entityIndex = 0; entityIndex < entities.Count; entityIndex++)
394
+ {
395
+ var authoring = entities[entityIndex];
396
+ if (authoring == null)
397
+ {
398
+ continue;
399
+ }
400
+
401
+ if (string.IsNullOrEmpty(authoring.StableId))
402
+ {
403
+ continue;
404
+ }
405
+
406
+ Entity runtimeEntity = runtimeWorld != null
407
+ ? runtimeWorld.ResolveEntity(authoring.StableId)
408
+ : default;
409
+ bool hasView = authoringHost.TryGetView(authoring.StableId, out _)
410
+ || (runtimeWorld != null
411
+ && runtimeWorld.World.IsAlive(runtimeEntity)
412
+ && runtimeWorld.TryGetView(runtimeEntity, out _));
413
+ if (hasView)
414
+ {
415
+ continue;
416
+ }
417
+
418
+ var key = new EntityKey(authoringHost.GetEntityId(), authoring.StableId);
419
+ _activeEntityKeys.Add(key);
420
+ var reference = runtimeWorld != null && runtimeWorld.World.IsAlive(runtimeEntity)
421
+ ? new NodeReference(authoringHost, authoring.StableId, runtimeWorld, runtimeEntity)
422
+ : new NodeReference(authoringHost, authoring.StableId);
423
+ if (_entityNodes.TryGetValue(key, out var entityNode))
424
+ {
425
+ if (entityNode.Reference.RuntimeWorld != reference.RuntimeWorld
426
+ || entityNode.Reference.RuntimeEntity != reference.RuntimeEntity)
427
+ {
428
+ changed = true;
429
+ }
430
+
431
+ entityNode.Reference = reference;
432
+ if (!string.Equals(entityNode.Name, authoring.Name))
433
+ {
434
+ CommandList.SetName(entityNode.Node, authoring.Name);
435
+ entityNode.Name = authoring.Name;
436
+ changed = true;
437
+ }
438
+
439
+ _entityNodes[key] = entityNode;
440
+ _nodes[entityNode.Node] = reference;
441
+ HierarchySelection.UpdateSelectedTarget(
442
+ this,
443
+ entityNode.Node,
444
+ authoringHost,
445
+ authoring.StableId,
446
+ reference.RuntimeWorld,
447
+ reference.RuntimeEntity);
448
+ continue;
449
+ }
450
+
451
+ CommandList.Add(sceneNode, out var newNode);
452
+ CommandList.SetName(newNode, authoring.Name);
453
+ var newReference = reference;
454
+ _entityNodes.Add(key, new EntityNode(newNode, newReference, authoring.Name));
455
+ _nodes.Add(newNode, newReference);
456
+ HierarchySelection.UpdateSelectedTarget(
457
+ this,
458
+ newNode,
459
+ authoringHost,
460
+ authoring.StableId,
461
+ newReference.RuntimeWorld,
462
+ newReference.RuntimeEntity);
463
+ changed = true;
464
+ }
465
+
466
+ if (runtimeWorld != null)
467
+ {
468
+ changed |= SynchronizeRuntimeEntities(authoringHost, sceneNode, runtimeWorld);
469
+ }
470
+ }
471
+
472
+ _removedEntityKeys.Clear();
473
+ foreach (var pair in _entityNodes)
474
+ {
475
+ if (!_activeEntityKeys.Contains(pair.Key))
476
+ {
477
+ _removedEntityKeys.Add(pair.Key);
478
+ }
479
+ }
480
+
481
+ for (var i = 0; i < _removedEntityKeys.Count; i++)
482
+ {
483
+ var key = _removedEntityKeys[i];
484
+ var entityNode = _entityNodes[key];
485
+ if (entityNode.Reference.IsRuntimeOnly)
486
+ {
487
+ HierarchySelection.ClearIfSelected(
488
+ entityNode.Reference.RuntimeWorld,
489
+ entityNode.Reference.RuntimeEntity);
490
+ }
491
+ else
492
+ {
493
+ HierarchySelection.ClearIfSelected(this, entityNode.Reference.Authoring, entityNode.Reference.StableId);
494
+ }
495
+ if (Hierarchy.Exists(entityNode.Node))
496
+ {
497
+ CommandList.Remove(entityNode.Node);
498
+ }
499
+
500
+ SelectionProxyBridge.Remove(this, entityNode.Node);
501
+ _nodes.Remove(entityNode.Node);
502
+ _entityNodes.Remove(key);
503
+ changed = true;
504
+ }
505
+
506
+ if (CommandList.Size > 0)
507
+ {
508
+ CommandList.Execute();
509
+ }
510
+
511
+ if (changed)
512
+ {
513
+ Hierarchy.Update();
514
+ }
515
+
516
+ return changed;
517
+ }
518
+
519
+ private bool SynchronizeRuntimeEntities(
520
+ SceneAuthoring authoring,
521
+ HierarchyNode sceneNode,
522
+ SceneWorld runtimeWorld)
523
+ {
524
+ Query query = runtimeWorld.World.WhereAll(NoRequiredComponents);
525
+ bool changed = false;
526
+ runtimeWorld.World.ForEachEntity(in query, entity =>
527
+ {
528
+ if (runtimeWorld.TryGetStableId(entity, out _) || runtimeWorld.TryGetView(entity, out _))
529
+ {
530
+ return;
531
+ }
532
+
533
+ var key = new EntityKey(runtimeWorld, entity);
534
+ _activeEntityKeys.Add(key);
535
+ string name = $"ECS Entity {entity.Index}:{entity.Generation}";
536
+ var reference = new NodeReference(authoring, null, runtimeWorld, entity);
537
+ if (_entityNodes.TryGetValue(key, out EntityNode entityNode))
538
+ {
539
+ if (entityNode.Reference.RuntimeWorld != reference.RuntimeWorld
540
+ || entityNode.Reference.RuntimeEntity != reference.RuntimeEntity)
541
+ {
542
+ entityNode.Reference = reference;
543
+ changed = true;
544
+ }
545
+
546
+ if (!string.Equals(entityNode.Name, name, StringComparison.Ordinal))
547
+ {
548
+ CommandList.SetName(entityNode.Node, name);
549
+ entityNode.Name = name;
550
+ changed = true;
551
+ }
552
+
553
+ _entityNodes[key] = entityNode;
554
+ _nodes[entityNode.Node] = reference;
555
+ return;
556
+ }
557
+
558
+ CommandList.Add(sceneNode, out HierarchyNode node);
559
+ CommandList.SetName(node, name);
560
+ _entityNodes.Add(key, new EntityNode(node, reference, name));
561
+ _nodes.Add(node, reference);
562
+ changed = true;
563
+ });
564
+
565
+ return changed;
566
+ }
567
+
568
+ private void SynchronizeSceneSubscriptions(SceneAuthoring[] authorings)
569
+ {
570
+ var loadedAuthorings = new HashSet<SceneAuthoring>();
571
+ for (var i = 0; i < authorings.Length; i++)
572
+ {
573
+ var authoring = authorings[i];
574
+ if (authoring == null || !IsHierarchyScene(authoring.gameObject.scene))
575
+ {
576
+ continue;
577
+ }
578
+
579
+ loadedAuthorings.Add(authoring);
580
+ if (!_sceneDataSubscriptions.ContainsKey(authoring))
581
+ {
582
+ var capturedAuthoring = authoring;
583
+ Action<SceneDataChange> callback = change => OnSceneDataChanged(capturedAuthoring, change);
584
+ _sceneDataSubscriptions.Add(authoring, callback);
585
+ authoring.SceneData.Changed += callback;
586
+ }
587
+ }
588
+
589
+ if (_sceneDataSubscriptions.Count == loadedAuthorings.Count)
590
+ {
591
+ return;
592
+ }
593
+
594
+ var removed = new List<SceneAuthoring>();
595
+ foreach (var authoring in _sceneDataSubscriptions.Keys)
596
+ {
597
+ if (!loadedAuthorings.Contains(authoring))
598
+ {
599
+ removed.Add(authoring);
600
+ }
601
+ }
602
+
603
+ for (var i = 0; i < removed.Count; i++)
604
+ {
605
+ var authoring = removed[i];
606
+ if (authoring != null)
607
+ {
608
+ authoring.SceneData.Changed -= _sceneDataSubscriptions[authoring];
609
+ }
610
+ _sceneDataSubscriptions.Remove(authoring);
611
+ }
612
+ }
613
+
614
+ private void OnSceneDataChanged(SceneAuthoring authoring, SceneDataChange change)
615
+ {
616
+ var changed = change.Kind switch
617
+ {
618
+ SceneDataChangeKind.EntityCreated => authoring.SceneData.TryGetEntity(change.StableId, out var entity) &&
619
+ AddEntity(authoring, entity),
620
+ SceneDataChangeKind.EntityDestroyed => RemoveEntity(authoring, change.StableId),
621
+ SceneDataChangeKind.EntityRenamed => RenameEntity(authoring, change.StableId),
622
+ _ => false
623
+ };
624
+
625
+ if (changed)
626
+ {
627
+ EditorApplication.RepaintHierarchyWindow();
628
+ }
629
+ }
630
+
631
+ private bool AddEntity(SceneAuthoring authoring, EntityAuthoring entity)
632
+ {
633
+ if (!Hierarchy.IsCreated || authoring == null || !IsHierarchyScene(authoring.gameObject.scene)
634
+ || string.IsNullOrEmpty(entity.StableId))
635
+ {
636
+ return false;
637
+ }
638
+
639
+ var key = new EntityKey(authoring.GetEntityId(), entity.StableId);
640
+ if (_entityNodes.ContainsKey(key))
641
+ {
642
+ return RenameEntity(authoring, entity.StableId);
643
+ }
644
+
645
+ var sceneHandler = Hierarchy.GetNodeTypeHandler<HierarchySceneHandler>();
646
+ var sceneNode = sceneHandler.GetOrCreateNode(authoring.gameObject.scene);
647
+ if (sceneNode == HierarchyNode.Null)
648
+ {
649
+ return false;
650
+ }
651
+
652
+ CommandList.Add(sceneNode, out var newNode);
653
+ CommandList.SetName(newNode, entity.Name);
654
+ CommandList.Execute();
655
+
656
+ var reference = new NodeReference(authoring, entity.StableId);
657
+ _entityNodes.Add(key, new EntityNode(newNode, reference, entity.Name));
658
+ _nodes.Add(newNode, reference);
659
+ Hierarchy.Update();
660
+ return true;
661
+ }
662
+
663
+ private bool RemoveEntity(SceneAuthoring authoring, string stableId)
664
+ {
665
+ if (authoring == null || string.IsNullOrEmpty(stableId))
666
+ {
667
+ return false;
668
+ }
669
+
670
+ var key = new EntityKey(authoring.GetEntityId(), stableId);
671
+ if (!_entityNodes.TryGetValue(key, out var entityNode))
672
+ {
673
+ return false;
674
+ }
675
+
676
+ HierarchySelection.ClearIfSelected(this, authoring, stableId);
677
+ if (Hierarchy.IsCreated && Hierarchy.Exists(entityNode.Node))
678
+ {
679
+ CommandList.Remove(entityNode.Node);
680
+ CommandList.Execute();
681
+ }
682
+
683
+ SelectionProxyBridge.Remove(this, entityNode.Node);
684
+ _nodes.Remove(entityNode.Node);
685
+ _entityNodes.Remove(key);
686
+ if (Hierarchy.IsCreated)
687
+ {
688
+ Hierarchy.Update();
689
+ }
690
+
691
+ return true;
692
+ }
693
+
694
+ private bool RenameEntity(SceneAuthoring authoring, string stableId)
695
+ {
696
+ if (authoring == null || string.IsNullOrEmpty(stableId))
697
+ {
698
+ return false;
699
+ }
700
+
701
+ var key = new EntityKey(authoring.GetEntityId(), stableId);
702
+ if (!_entityNodes.TryGetValue(key, out var entityNode)
703
+ || !authoring.SceneData.TryGetEntity(stableId, out var entity)
704
+ || string.Equals(entityNode.Name, entity.Name))
705
+ {
706
+ return false;
707
+ }
708
+
709
+ CommandList.SetName(entityNode.Node, entity.Name);
710
+ CommandList.Execute();
711
+ entityNode.Name = entity.Name;
712
+ _entityNodes[key] = entityNode;
713
+ HierarchySelection.RenameSelected(authoring, stableId);
714
+ Hierarchy.Update();
715
+ return true;
716
+ }
717
+
718
+ internal static void SelectEntity(HierarchyView view, SceneAuthoring authoring, string stableId)
719
+ {
720
+ var key = new EntityKey(authoring.GetEntityId(), stableId);
721
+ foreach (var instance in Instances)
722
+ {
723
+ if (instance._entityNodes.ContainsKey(key))
724
+ {
725
+ if (view != null)
726
+ {
727
+ if (instance._boundViews.Contains(view))
728
+ {
729
+ TrySelectEntity(instance, view, key);
730
+ }
731
+ }
732
+ else
733
+ {
734
+ foreach (var boundView in instance._boundViews)
735
+ {
736
+ if (TrySelectEntity(instance, boundView, key))
737
+ {
738
+ break;
739
+ }
740
+ }
741
+ }
742
+
743
+ return;
744
+ }
745
+ }
746
+ }
747
+
748
+ private static bool TrySelectEntity(
749
+ HierarchyNodeHandler handler,
750
+ HierarchyView view,
751
+ EntityKey key)
752
+ {
753
+ if (view == null || !handler._boundViews.Contains(view)
754
+ || !handler._entityNodes.TryGetValue(key, out var entityNode)
755
+ || !handler.Hierarchy.Exists(entityNode.Node))
756
+ {
757
+ return false;
758
+ }
759
+
760
+ if (view.UpdateNeeded)
761
+ {
762
+ view.Update();
763
+ }
764
+
765
+ var viewModel = view.ViewModel;
766
+ if (!viewModel.Contains(in entityNode.Node))
767
+ {
768
+ return false;
769
+ }
770
+
771
+ var node = entityNode.Node;
772
+ view.SetSelection(in node);
773
+ return true;
774
+ }
775
+
776
+ private void PopulateContextMenu(HierarchyView view, HierarchyViewItem item, DropdownMenu menu)
777
+ {
778
+ if (EditorApplication.isPlayingOrWillChangePlaymode)
779
+ {
780
+ return;
781
+ }
782
+
783
+ if (item != null && _nodes.TryGetValue(item.Node, out var node))
784
+ {
785
+ if (node.IsEntity)
786
+ {
787
+ var entityNode = item.Node;
788
+ menu.AppendAction("Select ECS Entity", _ => view.SetSelection(in entityNode),
789
+ _ => DropdownMenuAction.Status.Normal);
790
+ menu.AppendAction("Delete ECS Entity", _ => HierarchyMenu.DeleteEntity(node.Authoring, node.StableId),
791
+ _ => DropdownMenuAction.Status.Normal);
792
+ }
793
+
794
+ menu.AppendAction("Create ECS Entity", _ => HierarchyMenu.CreateEntity(node.Authoring, view),
795
+ _ => DropdownMenuAction.Status.Normal);
796
+ return;
797
+ }
798
+
799
+ var scene = item != null
800
+ && view.Source.GetNodeTypeHandler(item.Node) is HierarchySceneHandler sceneHandler
801
+ ? sceneHandler.GetScene(item.Node)
802
+ : SceneManager.GetActiveScene();
803
+ var authoringHost = FindAuthoringForScene(scene);
804
+ if (authoringHost != null)
805
+ {
806
+ menu.AppendAction("Create Delta ECS Entity", _ => HierarchyMenu.CreateEntity(authoringHost, view),
807
+ _ => DropdownMenuAction.Status.Normal);
808
+ }
809
+ }
810
+
811
+ private void OnHierarchyFlagsChanged(HierarchyView view, HierarchyNodeFlags changedFlags)
812
+ {
813
+ if ((changedFlags & HierarchyNodeFlags.Selected) == 0)
814
+ {
815
+ return;
816
+ }
817
+
818
+ NodeReference selectedReference = default;
819
+ var selectedEntityCount = 0;
820
+ foreach (ref readonly var node in view.ViewModel.EnumerateNodesWithFlags(HierarchyNodeFlags.Selected))
821
+ {
822
+ if (!_nodes.TryGetValue(node, out var reference) || !reference.IsEntity)
823
+ {
824
+ continue;
825
+ }
826
+
827
+ selectedEntityCount++;
828
+ if (selectedEntityCount == 1)
829
+ {
830
+ selectedReference = reference;
831
+ }
832
+ }
833
+
834
+ if (selectedEntityCount == 1)
835
+ {
836
+ EntityKey selectedKey = selectedReference.StableId != null
837
+ ? new EntityKey(selectedReference.Authoring.GetEntityId(), selectedReference.StableId)
838
+ : new EntityKey(selectedReference.RuntimeWorld, selectedReference.RuntimeEntity);
839
+ if (!_entityNodes.TryGetValue(selectedKey, out EntityNode selectedNode))
840
+ {
841
+ HierarchySelection.DeferClear(view);
842
+ return;
843
+ }
844
+
845
+ HierarchySelection.SetFromHierarchy(
846
+ this,
847
+ view,
848
+ selectedReference.Authoring,
849
+ selectedReference.StableId,
850
+ selectedReference.RuntimeWorld,
851
+ selectedReference.RuntimeEntity,
852
+ selectedNode.Node);
853
+ SelectionProxyBridge.Select(this, selectedNode.Node,
854
+ new EntityInspectorTarget(selectedReference.Authoring, selectedReference.StableId,
855
+ selectedReference.RuntimeWorld, selectedReference.RuntimeEntity));
856
+ }
857
+ else if (selectedEntityCount > 1)
858
+ {
859
+ HierarchySelection.Clear(view);
860
+ }
861
+ else
862
+ {
863
+ HierarchySelection.DeferClear(view);
864
+ }
865
+ }
866
+
867
+ private readonly struct NodeReference
868
+ {
869
+ public readonly SceneAuthoring Authoring;
870
+ public readonly string StableId;
871
+ public readonly SceneWorld RuntimeWorld;
872
+ public readonly Entity RuntimeEntity;
873
+ public bool IsEntity => !string.IsNullOrEmpty(StableId) || RuntimeWorld != null;
874
+ public bool IsRuntimeOnly => string.IsNullOrEmpty(StableId) && RuntimeWorld != null;
875
+
876
+ public NodeReference(SceneAuthoring authoring, string stableId)
877
+ : this(authoring, stableId, null, default)
878
+ {
879
+ }
880
+
881
+ public NodeReference(
882
+ SceneAuthoring authoring,
883
+ string stableId,
884
+ SceneWorld runtimeWorld,
885
+ Entity runtimeEntity)
886
+ {
887
+ Authoring = authoring;
888
+ StableId = stableId;
889
+ RuntimeWorld = runtimeWorld;
890
+ RuntimeEntity = runtimeEntity;
891
+ }
892
+ }
893
+
894
+ private struct EntityNode
895
+ {
896
+ public readonly HierarchyNode Node;
897
+ public NodeReference Reference;
898
+ public string Name;
899
+
900
+ public EntityNode(HierarchyNode node, NodeReference reference, string name)
901
+ {
902
+ Node = node;
903
+ Reference = reference;
904
+ Name = name;
905
+ }
906
+ }
907
+
908
+ private readonly struct EntityKey : IEquatable<EntityKey>
909
+ {
910
+ private readonly EntityId _authoringId;
911
+ private readonly string _stableId;
912
+ private readonly SceneWorld _runtimeWorld;
913
+ private readonly Entity _runtimeEntity;
914
+
915
+ public EntityKey(EntityId authoringId, string stableId)
916
+ {
917
+ _authoringId = authoringId;
918
+ _stableId = stableId;
919
+ _runtimeWorld = null;
920
+ _runtimeEntity = default;
921
+ }
922
+
923
+ public EntityKey(SceneWorld runtimeWorld, Entity runtimeEntity)
924
+ {
925
+ _authoringId = default;
926
+ _stableId = null;
927
+ _runtimeWorld = runtimeWorld;
928
+ _runtimeEntity = runtimeEntity;
929
+ }
930
+
931
+ public bool Equals(EntityKey other)
932
+ {
933
+ if (_runtimeWorld != null || other._runtimeWorld != null)
934
+ {
935
+ return ReferenceEquals(_runtimeWorld, other._runtimeWorld)
936
+ && _runtimeEntity.Equals(other._runtimeEntity);
937
+ }
938
+
939
+ return _authoringId.Equals(other._authoringId)
940
+ && string.Equals(_stableId, other._stableId, StringComparison.Ordinal);
941
+ }
942
+
943
+ public override bool Equals(object obj) => obj is EntityKey other && Equals(other);
944
+ public override int GetHashCode() => _runtimeWorld != null
945
+ ? unchecked((_runtimeWorld.GetHashCode() * 397) ^ _runtimeEntity.GetHashCode())
946
+ : unchecked((_authoringId.GetHashCode() * 397) ^ StringComparer.Ordinal.GetHashCode(_stableId ?? string.Empty));
947
+ }
948
+ }
949
+
950
+ }