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,618 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using Delta.ECS;
4
+ using Delta.ECS.Integration;
5
+ using UnityEngine;
6
+ using UnityEngine.SceneManagement;
7
+
8
+ namespace Delta.ECS.Unity
9
+ {
10
+ /// <summary>
11
+ /// Binds serialized scene authoring data to a DeltaECS world. This is deliberately
12
+ /// a plain C# object: it does not add a world GameObject to the scene.
13
+ /// </summary>
14
+ public sealed class SceneWorld : IDisposable
15
+ {
16
+ private const int DefaultEntityCapacity = 256;
17
+ private const int RuntimeEntityCapacity = 4096;
18
+ private static readonly List<SceneWorld> LiveWorlds = new();
19
+
20
+ private readonly Dictionary<string, Entity> _entitiesByStableId = new(StringComparer.Ordinal);
21
+ private readonly Dictionary<Entity, string> _stableIdsByEntity = new();
22
+ private readonly Dictionary<Entity, GameObject> _viewsByEntity = new();
23
+ private readonly Dictionary<UnityEngine.EntityId, Entity> _entitiesByView = new();
24
+ private readonly World _world;
25
+ private readonly IEcsWorld _integration;
26
+ private readonly SceneData _sceneData;
27
+ private readonly SceneAuthoring _authoring;
28
+ private readonly bool _ownsWorld;
29
+ private bool _disposed;
30
+
31
+ public World World => _world;
32
+ internal IEcsWorld Integration => _integration;
33
+ internal SceneData SceneData => _sceneData;
34
+ internal bool IsBound => !_disposed;
35
+
36
+ /// <summary>Returns the integration-owned runtime context for a loaded Unity scene.</summary>
37
+ public static SceneWorld GetForScene(Scene scene) => SceneWorldRuntime.GetForScene(scene);
38
+
39
+ internal static bool TryFindLiveWorld(SceneData sceneData, out SceneWorld sceneWorld)
40
+ {
41
+ for (int i = LiveWorlds.Count - 1; i >= 0; i--)
42
+ {
43
+ SceneWorld candidate = LiveWorlds[i];
44
+ if (!candidate._disposed && ReferenceEquals(candidate._sceneData, sceneData))
45
+ {
46
+ sceneWorld = candidate;
47
+ return true;
48
+ }
49
+ }
50
+
51
+ sceneWorld = null;
52
+ return false;
53
+ }
54
+
55
+ internal static bool TryFindLiveWorld(GameObject view, out SceneWorld sceneWorld, out Entity entity)
56
+ {
57
+ for (int i = LiveWorlds.Count - 1; i >= 0; i--)
58
+ {
59
+ SceneWorld candidate = LiveWorlds[i];
60
+ if (!candidate._disposed && candidate.TryGetEntity(view, out entity))
61
+ {
62
+ sceneWorld = candidate;
63
+ return true;
64
+ }
65
+ }
66
+
67
+ sceneWorld = null;
68
+ entity = default;
69
+ return false;
70
+ }
71
+
72
+ public Entity[] AuthoredEntities
73
+ {
74
+ get
75
+ {
76
+ var entities = new Entity[_entitiesByStableId.Count];
77
+ _entitiesByStableId.Values.CopyTo(entities, 0);
78
+ return entities;
79
+ }
80
+ }
81
+
82
+ internal SceneWorld(World world, SceneData sceneData)
83
+ : this(world, sceneData, null, ownsWorld: false, registerLiveWorld: true)
84
+ {
85
+ }
86
+
87
+ private SceneWorld(World world, SceneData sceneData,
88
+ SceneAuthoring authoring, bool ownsWorld, bool registerLiveWorld)
89
+ {
90
+ _world = world ?? UnityThrowHelpers.ThrowArgumentNull<World>(nameof(world));
91
+ _sceneData = sceneData ?? UnityThrowHelpers.ThrowArgumentNull<SceneData>(nameof(sceneData));
92
+ _authoring = authoring;
93
+ _integration = world;
94
+ _ownsWorld = ownsWorld;
95
+
96
+ _sceneData.EnsureStableIds();
97
+ _integration.Initialize();
98
+ RebuildAuthoringEntities();
99
+ if (registerLiveWorld)
100
+ {
101
+ LiveWorlds.Add(this);
102
+ }
103
+ }
104
+
105
+ internal static SceneWorld CreatePreview(SceneData sceneData)
106
+ {
107
+ var world = new World(GeneratedComponentCatalog.CreateLayoutRegistry(), DefaultEntityCapacity);
108
+ try
109
+ {
110
+ return new SceneWorld(world, sceneData, null,
111
+ ownsWorld: true, registerLiveWorld: false);
112
+ }
113
+ catch (Exception exception)
114
+ {
115
+ world.Dispose();
116
+ return UnityThrowHelpers.Rethrow<SceneWorld>(exception);
117
+ }
118
+ }
119
+
120
+ internal static SceneWorld CreateAuthoringPreview(SceneAuthoring authoring)
121
+ {
122
+ if (authoring == null)
123
+ {
124
+ UnityThrowHelpers.ThrowArgumentNull(nameof(authoring));
125
+ }
126
+
127
+ var world = new World(GeneratedComponentCatalog.CreateLayoutRegistry(), DefaultEntityCapacity);
128
+ try
129
+ {
130
+ return new SceneWorld(world, authoring.SceneData, authoring,
131
+ ownsWorld: true, registerLiveWorld: false);
132
+ }
133
+ catch (Exception exception)
134
+ {
135
+ world.Dispose();
136
+ return UnityThrowHelpers.Rethrow<SceneWorld>(exception);
137
+ }
138
+ }
139
+
140
+ internal static SceneWorld CreateRuntime(SceneAuthoring authoring)
141
+ {
142
+ if (authoring == null)
143
+ {
144
+ UnityThrowHelpers.ThrowArgumentNull(nameof(authoring));
145
+ }
146
+
147
+ var world = new World(GeneratedComponentCatalog.CreateLayoutRegistry(), RuntimeEntityCapacity);
148
+ try
149
+ {
150
+ return new SceneWorld(world, authoring.SceneData, authoring,
151
+ ownsWorld: true, registerLiveWorld: true);
152
+ }
153
+ catch (Exception exception)
154
+ {
155
+ world.Dispose();
156
+ return UnityThrowHelpers.Rethrow<SceneWorld>(exception);
157
+ }
158
+ }
159
+
160
+ internal void RebuildAuthoringEntities()
161
+ {
162
+ ThrowIfDisposed();
163
+ DestroyCurrentAuthoringEntities();
164
+ _entitiesByStableId.Clear();
165
+ _stableIdsByEntity.Clear();
166
+
167
+ _sceneData.EnsureStableIds();
168
+ IList<EntityAuthoring> authorings = _sceneData.Entities;
169
+ for (int i = 0; i < authorings.Count; i++)
170
+ {
171
+ EntityAuthoring authoring = authorings[i];
172
+ if (authoring == null)
173
+ {
174
+ continue;
175
+ }
176
+
177
+ var componentIds = new List<ComponentId>(authoring.Components.Count);
178
+ var seenSchemas = new HashSet<ulong>();
179
+ for (int componentIndex = 0; componentIndex < authoring.Components.Count; componentIndex++)
180
+ {
181
+ ComponentData data = authoring.Components[componentIndex];
182
+ if (TryGetDescriptor(data, out ComponentDescriptor descriptor)
183
+ && seenSchemas.Add(descriptor.Schema.Value))
184
+ {
185
+ componentIds.Add(descriptor.Id);
186
+ }
187
+ else
188
+ {
189
+ Debug.LogWarning($"Unknown, duplicate, or invalid component schema '{data?.SchemaIdText}' on '{authoring.Name}'.");
190
+ }
191
+ }
192
+
193
+ Entity entity = _integration.Create(componentIds.ToArray());
194
+ _entitiesByStableId.Add(authoring.StableId, entity);
195
+ _stableIdsByEntity.Add(entity, authoring.StableId);
196
+ }
197
+
198
+ // Create every entity before loading values so stable-ID references can resolve.
199
+ for (int i = 0; i < authorings.Count; i++)
200
+ {
201
+ EntityAuthoring authoring = authorings[i];
202
+ if (authoring == null || !_entitiesByStableId.TryGetValue(authoring.StableId, out Entity entity))
203
+ {
204
+ continue;
205
+ }
206
+
207
+ for (int componentIndex = 0; componentIndex < authoring.Components.Count; componentIndex++)
208
+ {
209
+ ComponentData data = authoring.Components[componentIndex];
210
+ if (!TryGetDescriptor(data, out ComponentDescriptor descriptor))
211
+ {
212
+ continue;
213
+ }
214
+
215
+ object value = data.CreateValue(descriptor.ValueType, ResolveEntity);
216
+ if (_integration.TryRead(entity, descriptor.Id, out ComponentSnapshot snapshot, out _)
217
+ && !_integration.TryWrite(entity, descriptor.Id, value, snapshot.Stamp, out _, out EcsWriteError error))
218
+ {
219
+ Debug.LogError($"Failed to load {descriptor.Name} on '{authoring.Name}': {error.Code}.");
220
+ }
221
+ }
222
+ }
223
+ }
224
+
225
+ internal bool TryGetEntity(EntityAuthoring authoring, out Entity entity)
226
+ {
227
+ if (authoring != null)
228
+ {
229
+ return _entitiesByStableId.TryGetValue(authoring.StableId, out entity);
230
+ }
231
+
232
+ entity = default;
233
+ return false;
234
+ }
235
+
236
+ internal Entity ResolveEntity(string stableId)
237
+ {
238
+ return !string.IsNullOrEmpty(stableId)
239
+ && _entitiesByStableId.TryGetValue(stableId, out Entity entity)
240
+ ? entity
241
+ : default;
242
+ }
243
+
244
+ internal bool TryGetStableId(Entity entity, out string stableId) =>
245
+ _stableIdsByEntity.TryGetValue(entity, out stableId);
246
+
247
+ /// <summary>Resolves the runtime view first, then the scene-authored stable-ID binding.</summary>
248
+ internal bool TryGetView(Entity entity, out GameObject view)
249
+ {
250
+ if (_viewsByEntity.TryGetValue(entity, out view) && view != null)
251
+ {
252
+ return true;
253
+ }
254
+
255
+ if (_viewsByEntity.ContainsKey(entity))
256
+ {
257
+ _viewsByEntity.Remove(entity);
258
+ }
259
+
260
+ return _authoring != null
261
+ && TryGetStableId(entity, out string stableId)
262
+ && _authoring.TryGetView(stableId, out view);
263
+ }
264
+
265
+ /// <summary>Resolves a view through the runtime registry or the scene-authored stable-ID binding.</summary>
266
+ internal bool TryGetEntity(GameObject view, out Entity entity)
267
+ {
268
+ if (view != null && _entitiesByView.TryGetValue(view.GetEntityId(), out entity)
269
+ && _integration.IsAlive(entity))
270
+ {
271
+ return true;
272
+ }
273
+
274
+ if (view != null && _authoring != null
275
+ && _authoring.TryGetStableId(view, out string stableId)
276
+ && _entitiesByStableId.TryGetValue(stableId, out entity)
277
+ && _integration.IsAlive(entity))
278
+ {
279
+ return true;
280
+ }
281
+
282
+ entity = default;
283
+ return false;
284
+ }
285
+
286
+ /// <summary>Registers a transient runtime association; it is not written into scene data.</summary>
287
+ public bool BindView(Entity entity, GameObject view)
288
+ {
289
+ if (view == null || !_integration.IsAlive(entity))
290
+ {
291
+ return false;
292
+ }
293
+
294
+ UnityEngine.EntityId viewId = view.GetEntityId();
295
+ if (_entitiesByView.TryGetValue(viewId, out Entity previousEntity))
296
+ {
297
+ _viewsByEntity.Remove(previousEntity);
298
+ }
299
+
300
+ if (_viewsByEntity.TryGetValue(entity, out GameObject previousView) && previousView != null)
301
+ {
302
+ _entitiesByView.Remove(previousView.GetEntityId());
303
+ }
304
+
305
+ _viewsByEntity[entity] = view;
306
+ _entitiesByView[viewId] = entity;
307
+ return true;
308
+ }
309
+
310
+ public void UnbindView(Entity entity)
311
+ {
312
+ if (_viewsByEntity.Remove(entity, out GameObject view) && view != null)
313
+ {
314
+ _entitiesByView.Remove(view.GetEntityId());
315
+ }
316
+ }
317
+
318
+ internal bool TryGetDescriptor(ComponentData data, out ComponentDescriptor descriptor)
319
+ {
320
+ descriptor = default;
321
+ if (data == null || !data.TryGetSchemaId(out SchemaId schema))
322
+ {
323
+ return false;
324
+ }
325
+
326
+ ReadOnlyMemory<ComponentDescriptor> catalog = _integration.Catalog.Components;
327
+ for (int i = 0; i < catalog.Length; i++)
328
+ {
329
+ if (catalog.Span[i].Schema == schema)
330
+ {
331
+ descriptor = catalog.Span[i];
332
+ return true;
333
+ }
334
+ }
335
+
336
+ return false;
337
+ }
338
+
339
+ internal bool TryRead(EntityAuthoring authoring, ComponentDescriptor descriptor,
340
+ out ComponentSnapshot snapshot, out EcsReadError error)
341
+ {
342
+ if (TryGetEntity(authoring, out Entity entity))
343
+ {
344
+ return _integration.TryRead(entity, descriptor.Id, out snapshot, out error);
345
+ }
346
+
347
+ snapshot = default;
348
+ error = default;
349
+ return false;
350
+ }
351
+
352
+ internal bool TryWrite(EntityAuthoring authoring, ComponentData data,
353
+ ComponentDescriptor descriptor, object value, out EcsWriteError error)
354
+ {
355
+ if (!TryGetEntity(authoring, out Entity entity)
356
+ || !_integration.TryRead(entity, descriptor.Id, out ComponentSnapshot snapshot, out _))
357
+ {
358
+ error = default;
359
+ return false;
360
+ }
361
+
362
+ if (!_integration.TryWrite(entity, descriptor.Id, value, snapshot.Stamp, out _, out error))
363
+ {
364
+ return false;
365
+ }
366
+
367
+ data.StoreValue(value, GetStableIdOrEmpty);
368
+ return true;
369
+ }
370
+
371
+ internal bool AddComponent(EntityAuthoring authoring, ComponentDescriptor descriptor,
372
+ ComponentData data, out EcsWriteError error, out string failureReason)
373
+ {
374
+ failureReason = null;
375
+ if (!TryGetEntity(authoring, out Entity entity))
376
+ {
377
+ error = new EcsWriteError(EcsWriteErrorCode.EntityNotAlive);
378
+ failureReason = "The authored entity is not bound to the preview world. Rebuild the preview and try again.";
379
+ return false;
380
+ }
381
+
382
+ if (!_integration.IsAlive(entity))
383
+ {
384
+ error = new EcsWriteError(EcsWriteErrorCode.EntityNotAlive);
385
+ failureReason = "The entity is no longer alive in the preview world.";
386
+ return false;
387
+ }
388
+
389
+ if (!_integration.Add(entity, new[] { descriptor.Id }))
390
+ {
391
+ error = default;
392
+ failureReason = _world.Has(entity, descriptor.Id)
393
+ ? "The component is already present in the preview world."
394
+ : "DeltaECS rejected the structural add without reporting a write error.";
395
+ return false;
396
+ }
397
+
398
+ object value = data.CreateValue(descriptor.ValueType, ResolveEntity);
399
+ if (!_integration.TryRead(entity, descriptor.Id, out ComponentSnapshot snapshot, out EcsReadError readError))
400
+ {
401
+ _integration.Remove(entity, new[] { descriptor.Id });
402
+ error = new EcsWriteError(readError.Code switch
403
+ {
404
+ EcsReadErrorCode.EntityNotAlive => EcsWriteErrorCode.EntityNotAlive,
405
+ EcsReadErrorCode.ComponentUnknown => EcsWriteErrorCode.ComponentUnknown,
406
+ EcsReadErrorCode.ComponentMissing => EcsWriteErrorCode.ComponentMissing,
407
+ EcsReadErrorCode.Unsupported => EcsWriteErrorCode.Unsupported,
408
+ _ => EcsWriteErrorCode.None
409
+ });
410
+ failureReason = $"The new component could not be read from the preview world ({readError.Code}).";
411
+ return false;
412
+ }
413
+
414
+ if (!_integration.TryWrite(entity, descriptor.Id, value, snapshot.Stamp, out _, out error))
415
+ {
416
+ _integration.Remove(entity, new[] { descriptor.Id });
417
+ failureReason = $"The default component value could not be written ({error.Code}).";
418
+ return false;
419
+ }
420
+
421
+ data.StoreValue(value, GetStableIdOrEmpty);
422
+ return true;
423
+ }
424
+
425
+ internal bool RemoveComponent(EntityAuthoring authoring, ComponentDescriptor descriptor,
426
+ out EcsWriteError error)
427
+ {
428
+ error = default;
429
+ return TryGetEntity(authoring, out Entity entity)
430
+ && _integration.Remove(entity, new[] { descriptor.Id });
431
+ }
432
+
433
+ public bool TryFindFirstEntity<T>(out Entity entity) where T : struct
434
+ {
435
+ if (_world.Layouts.TryGetPrimary(typeof(T), out ComponentId componentId))
436
+ {
437
+ foreach (Entity candidate in _entitiesByStableId.Values)
438
+ {
439
+ if (_integration.IsAlive(candidate) && _world.Has(candidate, componentId))
440
+ {
441
+ entity = candidate;
442
+ return true;
443
+ }
444
+ }
445
+ }
446
+
447
+ entity = default;
448
+ return false;
449
+ }
450
+
451
+ public bool HasComponent<T>(Entity entity) where T : struct =>
452
+ _world.IsAlive(entity)
453
+ && _world.Layouts.TryGetPrimary(typeof(T), out ComponentId componentId)
454
+ && _world.Has(entity, componentId);
455
+
456
+ internal bool TryGetAuthoring(string stableId, out EntityAuthoring authoring)
457
+ {
458
+ IList<EntityAuthoring> entities = _sceneData.Entities;
459
+ for (int i = 0; i < entities.Count; i++)
460
+ {
461
+ EntityAuthoring candidate = entities[i];
462
+ if (candidate != null && string.Equals(candidate.StableId, stableId, StringComparison.Ordinal))
463
+ {
464
+ authoring = candidate;
465
+ return true;
466
+ }
467
+ }
468
+
469
+ authoring = null;
470
+ return false;
471
+ }
472
+
473
+ public void Dispose()
474
+ {
475
+ if (_disposed)
476
+ {
477
+ return;
478
+ }
479
+
480
+ _disposed = true;
481
+ LiveWorlds.Remove(this);
482
+ DestroyCurrentAuthoringEntities();
483
+ _entitiesByStableId.Clear();
484
+ _stableIdsByEntity.Clear();
485
+ _viewsByEntity.Clear();
486
+ _entitiesByView.Clear();
487
+ try
488
+ {
489
+ _integration.Shutdown();
490
+ }
491
+ catch (InvalidOperationException)
492
+ {
493
+ // The ECS integration may already have been shut down during domain teardown.
494
+ }
495
+
496
+ if (_ownsWorld)
497
+ {
498
+ _world.Dispose();
499
+ }
500
+ }
501
+
502
+ private void DestroyCurrentAuthoringEntities()
503
+ {
504
+ foreach (Entity entity in _entitiesByStableId.Values)
505
+ {
506
+ if (_integration.IsAlive(entity))
507
+ {
508
+ _integration.Destroy(entity);
509
+ }
510
+ }
511
+ }
512
+
513
+ private string GetStableIdOrEmpty(Entity entity) =>
514
+ _stableIdsByEntity.TryGetValue(entity, out string stableId) ? stableId : string.Empty;
515
+
516
+ private void ThrowIfDisposed()
517
+ {
518
+ if (_disposed)
519
+ {
520
+ UnityThrowHelpers.ThrowObjectDisposed(nameof(SceneWorld));
521
+ }
522
+ }
523
+ }
524
+
525
+ /// <summary>Owns runtime world creation and teardown for loaded Unity scenes.</summary>
526
+ internal static class SceneWorldRuntime
527
+ {
528
+ private static readonly Dictionary<Scene, SceneWorld> WorldsByScene = new();
529
+ private static bool _initialized;
530
+
531
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
532
+ private static void ResetForPlaySession()
533
+ {
534
+ Shutdown();
535
+ }
536
+
537
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
538
+ private static void InitializeAfterSceneLoad()
539
+ {
540
+ EnsureInitialized();
541
+ for (int i = 0; i < SceneManager.sceneCount; i++)
542
+ {
543
+ EnsureForScene(SceneManager.GetSceneAt(i));
544
+ }
545
+ }
546
+
547
+ internal static SceneWorld GetForScene(Scene scene)
548
+ {
549
+ if (!Application.isPlaying)
550
+ {
551
+ UnityThrowHelpers.ThrowInvalidOperation("Runtime scene worlds are only available in Play Mode.");
552
+ }
553
+
554
+ EnsureInitialized();
555
+ return EnsureForScene(scene);
556
+ }
557
+
558
+ private static void EnsureInitialized()
559
+ {
560
+ if (_initialized)
561
+ {
562
+ return;
563
+ }
564
+
565
+ _initialized = true;
566
+ SceneManager.sceneLoaded += OnSceneLoaded;
567
+ SceneManager.sceneUnloaded += OnSceneUnloaded;
568
+ Application.quitting += Shutdown;
569
+ }
570
+
571
+ private static SceneWorld EnsureForScene(Scene scene)
572
+ {
573
+ if (!scene.IsValid() || !scene.isLoaded)
574
+ {
575
+ UnityThrowHelpers.ThrowArgument("A runtime ECS world requires a valid, loaded scene.", nameof(scene));
576
+ }
577
+
578
+ if (WorldsByScene.TryGetValue(scene, out SceneWorld existing)
579
+ && existing != null && existing.IsBound)
580
+ {
581
+ return existing;
582
+ }
583
+
584
+ SceneAuthoring authoring = SceneAuthoring.GetOrCreateForScene(scene);
585
+ SceneWorld sceneWorld = SceneWorld.CreateRuntime(authoring);
586
+ WorldsByScene[scene] = sceneWorld;
587
+ return sceneWorld;
588
+ }
589
+
590
+ private static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
591
+ {
592
+ EnsureForScene(scene);
593
+ }
594
+
595
+ private static void OnSceneUnloaded(Scene scene)
596
+ {
597
+ if (WorldsByScene.Remove(scene, out SceneWorld sceneWorld))
598
+ {
599
+ sceneWorld.Dispose();
600
+ }
601
+ }
602
+
603
+ private static void Shutdown()
604
+ {
605
+ SceneManager.sceneLoaded -= OnSceneLoaded;
606
+ SceneManager.sceneUnloaded -= OnSceneUnloaded;
607
+ Application.quitting -= Shutdown;
608
+ _initialized = false;
609
+
610
+ foreach (SceneWorld sceneWorld in WorldsByScene.Values)
611
+ {
612
+ sceneWorld.Dispose();
613
+ }
614
+
615
+ WorldsByScene.Clear();
616
+ }
617
+ }
618
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 5c14513dca0c4bde9a57fe850022826c
@@ -0,0 +1,44 @@
1
+ using System;
2
+ using System.Diagnostics.CodeAnalysis;
3
+ using System.Runtime.ExceptionServices;
4
+
5
+ namespace Delta.ECS.Unity
6
+ {
7
+ internal static class UnityThrowHelpers
8
+ {
9
+ internal static T ThrowArgumentNull<T>(string parameterName)
10
+ {
11
+ throw new ArgumentNullException(parameterName);
12
+ }
13
+
14
+ [DoesNotReturn]
15
+ internal static void ThrowArgumentNull(string parameterName)
16
+ {
17
+ throw new ArgumentNullException(parameterName);
18
+ }
19
+
20
+ [DoesNotReturn]
21
+ internal static void ThrowArgument(string message, string parameterName)
22
+ {
23
+ throw new ArgumentException(message, parameterName);
24
+ }
25
+
26
+ [DoesNotReturn]
27
+ internal static void ThrowInvalidOperation(string message)
28
+ {
29
+ throw new InvalidOperationException(message);
30
+ }
31
+
32
+ [DoesNotReturn]
33
+ internal static void ThrowObjectDisposed(string objectName)
34
+ {
35
+ throw new ObjectDisposedException(objectName);
36
+ }
37
+
38
+ internal static T Rethrow<T>(Exception exception)
39
+ {
40
+ ExceptionDispatchInfo.Capture(exception).Throw();
41
+ return default;
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 9134484d58754f818d4673db3a133a1f
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "com.artromskiy.deltaecs.unity",
3
+ "version": "0.0.37",
4
+ "displayName": "DeltaECS Unity",
5
+ "description": "Unity scene authoring and editor integration for DeltaECS.",
6
+ "unity": "6000.7",
7
+ "keywords": [
8
+ "ecs",
9
+ "entity-component-system",
10
+ "unity"
11
+ ],
12
+ "author": {
13
+ "name": "Artromskiy"
14
+ },
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/Artromskiy/DeltaECS.git",
19
+ "directory": "src/DeltaECS.Unity"
20
+ },
21
+ "publishConfig": {
22
+ "registry": "https://registry.npmjs.org/"
23
+ },
24
+ "files": [
25
+ "Runtime/",
26
+ "Editor/",
27
+ "README.md",
28
+ "CHANGELOG.md",
29
+ "LICENSE.md"
30
+ ]
31
+ }