Refactor Scene/SceneID to value-based model, move loading

Refactored Scene and SceneID to use a value-based approach, with SceneID now storing a ushort value instead of a Scene struct. Updated all usages to reference the new value and ID properties. Moved scene loading logic from SceneLoader (SceneAssetEntry.cs) into SceneManager for better consolidation. Updated entity creation, serialization, and tests to use the new SceneID pattern. Cleaned up obsolete code and improved comments regarding scene streaming and loading workflow. Added IDisposable to Scene and related structs for resource management.
This commit is contained in:
2026-05-13 19:48:31 +09:00
parent cbaa129d9e
commit 60b807abd7
9 changed files with 276 additions and 236 deletions

View File

@@ -35,7 +35,7 @@ public static class SceneGraphBuilder
for (var i = 0; i < chunk.EntityCount; i++) for (var i = 0; i < chunk.EntityCount; i++)
{ {
var s = sceneIDs[i].scene; var s = sceneIDs[i].value;
if (!s.IsValid) if (!s.IsValid)
{ {
continue; continue;
@@ -151,6 +151,6 @@ public static class SceneGraphBuilder
private static string GetDefaultSceneName(Scene scene) private static string GetDefaultSceneName(Scene scene)
{ {
return $"NewScene ({scene.id})"; return $"NewScene ({scene.ID})";
} }
} }

View File

@@ -31,7 +31,7 @@ public class EditorWorldService : IDisposable
var entity = EditorWorld.EntityManager.CreateEntity(); var entity = EditorWorld.EntityManager.CreateEntity();
EditorWorld.EntityManager.AddComponent(entity, new Engine.Components.SceneID EditorWorld.EntityManager.AddComponent(entity, new Engine.Components.SceneID
{ {
scene = scene value = scene
}); });
} }

View File

@@ -55,7 +55,7 @@ public class SceneGraphSyncService
} }
} }
var sceneName = $"NewScene ({scene.id})"; var sceneName = $"NewScene ({scene.ID})";
var newSceneNode = new SceneNode(world, scene, sceneName); var newSceneNode = new SceneNode(world, scene, sceneName);
rootNodes.Add(newSceneNode); rootNodes.Add(newSceneNode);
return newSceneNode; return newSceneNode;
@@ -183,7 +183,7 @@ public class SceneGraphSyncService
for (var i = 0; i < chunk.EntityCount; i++) for (var i = 0; i < chunk.EntityCount; i++)
{ {
var s = sceneIDs[i].scene; var s = sceneIDs[i].value;
if (!s.IsValid) if (!s.IsValid)
{ {
continue; continue;

View File

@@ -351,7 +351,7 @@ internal class SceneSerializationService : IDisposable
continue; continue;
} }
world.EntityManager.SetComponent(entity, new SceneID { scene = activeScene }); world.EntityManager.SetComponent(entity, new SceneID { value = activeScene });
var entityData = data.Entities[fileIndex]; var entityData = data.Entities[fileIndex];
ref var list = ref typeIds[fileIndex]; ref var list = ref typeIds[fileIndex];

View File

@@ -1,9 +1,8 @@
using Ghost.Engine.Core;
using Ghost.Entities; using Ghost.Entities;
namespace Ghost.Engine.Components; namespace Ghost.Engine.Components;
public struct SceneID : IComponent // TODO: ISharedComponent public struct SceneID : IComponent // TODO: ISharedComponent
{ {
public Scene scene; public ushort value;
} }

View File

@@ -1,31 +1,59 @@
using Ghost.Core;
using Ghost.Core.Utilities;
using Ghost.Engine.Components;
using Ghost.Engine.Streaming;
using Ghost.Entities; using Ghost.Entities;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Text.Json.Serialization; using System.Text;
namespace Ghost.Engine.Core; namespace Ghost.Engine.Core;
internal struct EntityNode : IDisposable
{
public struct ComponentSizeInfo
{
public nuint size;
public nuint alignment;
public nuint offset;
public Identifier<IComponent> id;
}
public UnsafeList<ComponentSizeInfo> infos;
public void Dispose()
{
infos.Dispose();
}
}
/// <summary> /// <summary>
/// Represents a runtime scene - a collection of entities with the same SceneID. /// Represents a runtime scene - a collection of entities with the same SceneID.
/// </summary> /// </summary>
public struct Scene : IEquatable<Scene> public struct Scene : IDisposable, IEquatable<Scene>
{ {
public ushort id; private ushort _id;
public readonly ushort ID => _id;
/// <summary> /// <summary>
/// Gets whether this scene is valid. /// Gets whether this scene is valid.
/// </summary> /// </summary>
[JsonIgnore] public readonly bool IsValid => ID != 65535;
public readonly bool IsValid => id != 65535;
/// <summary> /// <summary>
/// Gets an invalid scene instance. /// Gets an invalid scene instance.
/// </summary> /// </summary>
public static Scene Invalid => new Scene { id = 65535 }; public static Scene Invalid => new Scene { _id = 65535 };
internal Scene(ushort id)
{
_id = id;
}
public readonly bool Equals(Scene other) public readonly bool Equals(Scene other)
{ {
return id == other.id; return ID == other.ID;
} }
public readonly override bool Equals(object? obj) public readonly override bool Equals(object? obj)
@@ -35,7 +63,12 @@ public struct Scene : IEquatable<Scene>
public readonly override int GetHashCode() public readonly override int GetHashCode()
{ {
return id.GetHashCode(); return ID.GetHashCode();
}
public readonly override string ToString()
{
return $"Scene(ID: {ID})";
} }
public static bool operator ==(Scene left, Scene right) public static bool operator ==(Scene left, Scene right)
@@ -48,9 +81,8 @@ public struct Scene : IEquatable<Scene>
return !left.Equals(right); return !left.Equals(right);
} }
public readonly override string ToString() public void Dispose()
{ {
return $"Scene(ID: {id})";
} }
} }
@@ -81,10 +113,206 @@ public static class SceneManager
id = s_nextSceneID++; id = s_nextSceneID++;
} }
return new Scene { id = id }; return new Scene(id);
} }
} }
private struct BinaryEntityInfo : IDisposable
{
public int entityIndex;
public int componentCount;
public struct ComponentInfo
{
public uint typeHash;
public Identifier<IComponent> typeID;
public int dataSize;
public int dataOffset;
public int entityFieldCount;
public UnsafeArray<int> entityFieldOffsets;
}
public UnsafeArray<ComponentInfo> components;
public void Dispose()
{
for (var i = 0; i < components.Length; i++)
{
components[i].entityFieldOffsets.Dispose();
}
components.Dispose();
}
}
private struct BinaryEntityInfoArray : IDisposable
{
public UnsafeArray<BinaryEntityInfo> data;
public readonly ref BinaryEntityInfo this [int index] => ref data[index];
public BinaryEntityInfoArray(int count, AllocationHandle handle)
{
data = new UnsafeArray<BinaryEntityInfo>(count, handle, AllocationOption.Clear);
}
public void Dispose()
{
for (var i = 0; i < data.Length; i++)
{
data[i].Dispose();
}
data.Dispose();
}
}
public static unsafe Result<int> LoadSceneIntoWorld(World world, SceneContentHeader header, void* pRawData, nuint dataSize)
{
var reader = new BufferReader((byte*)pRawData, dataSize);
using var scope = AllocationManager.CreateStackScope();
using var entityInfos = new BinaryEntityInfoArray(header.entityCount, scope.AllocationHandle);
using var forwardMap = new UnsafeHashMap<int, Entity>(header.entityCount, scope.AllocationHandle);
for (var i = 0; i < header.entityCount; i++)
{
var compCount = reader.Read<int>();
if (compCount == 0)
{
continue;
}
var comps = new UnsafeArray<BinaryEntityInfo.ComponentInfo>(compCount, scope.AllocationHandle);
for (var j = 0; j < compCount; j++)
{
var typeHash = reader.Read<uint>();
var nameLength = reader.Read<int>();
var typeName = Encoding.UTF8.GetString(reader.ReadSpan<byte>(nameLength));
var dataSz = reader.Read<int>();
var dataOff = reader.Position;
reader.Position += (nuint)dataSz;
var fieldCount = reader.Read<int>();
var fieldOffsets = new UnsafeArray<int>(fieldCount, scope.AllocationHandle);
for (var f = 0; f < fieldCount; f++)
{
fieldOffsets[f] = reader.Read<int>();
}
var typeID = ComponentRegistry.GetComponentIDByName(typeName);
comps[j] = new BinaryEntityInfo.ComponentInfo
{
typeHash = typeHash,
typeID = typeID,
dataSize = dataSz,
dataOffset = (int)dataOff,
entityFieldCount = fieldCount,
entityFieldOffsets = fieldOffsets,
};
}
entityInfos[i] = new BinaryEntityInfo
{
entityIndex = i,
componentCount = compCount,
components = comps,
};
}
using var typeIds = new UnsafeList<Identifier<IComponent>>(32, scope.AllocationHandle);
typeIds.Add(ComponentTypeID<SceneID>.Value);
for (var i = 0; i < header.entityCount; i++)
{
ref var info = ref entityInfos[i];
for (var j = 0; j < info.componentCount; j++)
{
if (info.components[j].typeID.IsValid)
{
typeIds.Add(info.components[j].typeID);
}
}
var set = new ComponentSetView(typeIds);
var entity = world.EntityManager.CreateEntity(set);
forwardMap.TryAdd(i, entity);
typeIds.RemoveRange(1, typeIds.Count - 1);
}
var activeScene = CreateScene();
for (var i = 0; i < header.entityCount; i++)
{
if (!forwardMap.TryGetValue(i, out var entity))
{
continue;
}
world.EntityManager.SetComponent(entity, new SceneID { value = activeScene.ID });
var info = entityInfos[i];
for (var j = 0; j < info.componentCount; j++)
{
var comp = info.components[j];
if (!comp.typeID.IsValid)
{
continue;
}
var compSize = ComponentRegistry.GetComponentInfo(comp.typeID).size;
var pSrc = (byte*)pRawData + comp.dataOffset;
world.EntityManager.SetComponent(entity, comp.typeID, pSrc);
}
}
for (var i = 0; i < header.entityCount; i++)
{
if (!forwardMap.TryGetValue(i, out var entity))
{
continue;
}
var info = entityInfos[i];
for (var j = 0; j < info.componentCount; j++)
{
var comp = info.components[j];
if (!comp.typeID.IsValid || comp.entityFieldCount == 0)
{
continue;
}
var pComponent = world.EntityManager.GetComponent(entity, comp.typeID);
if (pComponent == null)
{
continue;
}
for (var f = 0; f < comp.entityFieldCount; f++)
{
var fieldOffset = comp.entityFieldOffsets[f];
var pField = (byte*)pComponent + fieldOffset;
var fileLocalIndex = *(int*)pField;
if (!forwardMap.TryGetValue(fileLocalIndex, out var remappedEntity))
{
remappedEntity = Entity.Invalid;
}
*(Entity*)pField = remappedEntity;
}
}
}
return Result.Success(header.entityCount);
}
/// <summary> /// <summary>
/// Destroys all entities belonging to the specified scene. /// Destroys all entities belonging to the specified scene.
/// </summary> /// </summary>
@@ -92,7 +320,7 @@ public static class SceneManager
/// <param name="world">The world containing the entities.</param> /// <param name="world">The world containing the entities.</param>
public static void UnloadScene(Scene scene, World world) public static void UnloadScene(Scene scene, World world)
{ {
var queryID = new QueryBuilder().WithAll<Components.SceneID>().Build(world); var queryID = new QueryBuilder().WithAll<SceneID>().Build(world);
ref var query = ref world.ComponentManager.GetEntityQueryReference(queryID); ref var query = ref world.ComponentManager.GetEntityQueryReference(queryID);
using var scope = AllocationManager.CreateStackScope(); using var scope = AllocationManager.CreateStackScope();
@@ -102,11 +330,11 @@ public static class SceneManager
foreach (var chunk in query.GetChunkIterator()) foreach (var chunk in query.GetChunkIterator())
{ {
var entities = chunk.GetEntities(); var entities = chunk.GetEntities();
var sceneIDs = chunk.GetComponentData<Components.SceneID>(); var sceneIDs = chunk.GetComponentData<SceneID>();
for (var i = 0; i < chunk.EntityCount; i++) for (var i = 0; i < chunk.EntityCount; i++)
{ {
if (sceneIDs[i].scene.id == scene.id) if (sceneIDs[i].value == scene.ID)
{ {
entitiesToDestroy.Add(entities[i]); entitiesToDestroy.Add(entities[i]);
} }
@@ -114,7 +342,13 @@ public static class SceneManager
} }
world.EntityManager.DestroyEntities(entitiesToDestroy.AsSpan()); world.EntityManager.DestroyEntities(entitiesToDestroy.AsSpan());
s_recycledSceneIDs.Enqueue(scene.id); s_recycledSceneIDs.Enqueue(scene.ID);
}
public static void ReleaseScene(Scene scene)
{
scene.Dispose();
s_recycledSceneIDs.Enqueue(scene.ID);
} }
/// <summary> /// <summary>
@@ -126,7 +360,7 @@ public static class SceneManager
/// <returns>The number of entities written to the span.</returns> /// <returns>The number of entities written to the span.</returns>
public static UnsafeList<Entity> GetSceneEntities(Scene scene, World world, AllocationHandle handle) public static UnsafeList<Entity> GetSceneEntities(Scene scene, World world, AllocationHandle handle)
{ {
var queryID = new QueryBuilder().WithAll<Components.SceneID>().Build(world); var queryID = new QueryBuilder().WithAll<SceneID>().Build(world);
ref var query = ref world.ComponentManager.GetEntityQueryReference(queryID); ref var query = ref world.ComponentManager.GetEntityQueryReference(queryID);
var entities = new UnsafeList<Entity>(128, handle); var entities = new UnsafeList<Entity>(128, handle);
@@ -135,11 +369,11 @@ public static class SceneManager
foreach (var chunk in query.GetChunkIterator()) foreach (var chunk in query.GetChunkIterator())
{ {
var chunkEntities = chunk.GetEntities(); var chunkEntities = chunk.GetEntities();
var sceneIDs = chunk.GetComponentData<Components.SceneID>(); var sceneIDs = chunk.GetComponentData<SceneID>();
for (var i = 0; i < chunk.EntityCount; i++) for (var i = 0; i < chunk.EntityCount; i++)
{ {
if (sceneIDs[i].scene.id == scene.id) if (sceneIDs[i].value == scene.ID)
{ {
entities.Add(chunkEntities[i]); entities.Add(chunkEntities[i]);
} }

View File

@@ -1,216 +1,16 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Utilities;
using Ghost.Engine.Components;
using Ghost.Engine.Core;
using Ghost.Entities; using Ghost.Entities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Ghost.Graphics.Services; using Ghost.Graphics.Services;
using Misaki.HighPerformance.Jobs; using Misaki.HighPerformance.Jobs;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
namespace Ghost.Engine.Streaming; namespace Ghost.Engine.Streaming;
internal static class SceneLoader internal static class SceneLoader
{ {
private struct BinaryEntityInfo : IDisposable
{
public int entityIndex;
public int componentCount;
public struct ComponentInfo
{
public uint typeHash;
public Identifier<IComponent> typeID;
public int dataSize;
public int dataOffset;
public int entityFieldCount;
public UnsafeArray<int> entityFieldOffsets;
}
public UnsafeArray<ComponentInfo> components;
public void Dispose()
{
for (var i = 0; i < components.Length; i++)
{
components[i].entityFieldOffsets.Dispose();
}
components.Dispose();
}
}
private struct BinaryEntityInfoArray : IDisposable
{
public UnsafeArray<BinaryEntityInfo> data;
public readonly ref BinaryEntityInfo this [int index] => ref data[index];
public BinaryEntityInfoArray(int count, AllocationHandle handle)
{
data = new UnsafeArray<BinaryEntityInfo>(count, handle, AllocationOption.Clear);
}
public void Dispose()
{
for (var i = 0; i < data.Length; i++)
{
data[i].Dispose();
}
data.Dispose();
}
}
public static unsafe Result<int> LoadSceneIntoWorld(World world, SceneContentHeader header, void* pRawData, nuint dataSize)
{
var reader = new BufferReader((byte*)pRawData, dataSize);
using var scope = AllocationManager.CreateStackScope();
using var entityInfos = new BinaryEntityInfoArray(header.entityCount, scope.AllocationHandle);
using var forwardMap = new UnsafeHashMap<int, Entity>(header.entityCount, scope.AllocationHandle);
for (var i = 0; i < header.entityCount; i++)
{
var compCount = reader.Read<int>();
if (compCount == 0)
{
continue;
}
var comps = new UnsafeArray<BinaryEntityInfo.ComponentInfo>(compCount, scope.AllocationHandle);
for (var j = 0; j < compCount; j++)
{
var typeHash = reader.Read<uint>();
var nameLength = reader.Read<int>();
var typeName = Encoding.UTF8.GetString(reader.ReadSpan<byte>(nameLength));
var dataSz = reader.Read<int>();
var dataOff = reader.Position;
reader.Position += (nuint)dataSz;
var fieldCount = reader.Read<int>();
var fieldOffsets = new UnsafeArray<int>(fieldCount, scope.AllocationHandle);
for (var f = 0; f < fieldCount; f++)
{
fieldOffsets[f] = reader.Read<int>();
}
var typeID = ComponentRegistry.GetComponentIDByName(typeName);
comps[j] = new BinaryEntityInfo.ComponentInfo
{
typeHash = typeHash,
typeID = typeID,
dataSize = dataSz,
dataOffset = (int)dataOff,
entityFieldCount = fieldCount,
entityFieldOffsets = fieldOffsets,
};
}
entityInfos[i] = new BinaryEntityInfo
{
entityIndex = i,
componentCount = compCount,
components = comps,
};
}
using var typeIds = new UnsafeList<Identifier<IComponent>>(32, scope.AllocationHandle);
typeIds.Add(ComponentTypeID<SceneID>.Value);
for (var i = 0; i < header.entityCount; i++)
{
ref var info = ref entityInfos[i];
for (var j = 0; j < info.componentCount; j++)
{
if (info.components[j].typeID.IsValid)
{
typeIds.Add(info.components[j].typeID);
}
}
var set = new ComponentSetView(typeIds);
var entity = world.EntityManager.CreateEntity(set);
forwardMap.TryAdd(i, entity);
typeIds.RemoveRange(1, typeIds.Count - 1);
}
var activeScene = SceneManager.CreateScene();
for (var i = 0; i < header.entityCount; i++)
{
if (!forwardMap.TryGetValue(i, out var entity))
{
continue;
}
world.EntityManager.SetComponent(entity, new SceneID { scene = activeScene });
var info = entityInfos[i];
for (var j = 0; j < info.componentCount; j++)
{
var comp = info.components[j];
if (!comp.typeID.IsValid)
{
continue;
}
var compSize = ComponentRegistry.GetComponentInfo(comp.typeID).size;
var pSrc = (byte*)pRawData + comp.dataOffset;
world.EntityManager.SetComponent(entity, comp.typeID, pSrc);
}
}
for (var i = 0; i < header.entityCount; i++)
{
if (!forwardMap.TryGetValue(i, out var entity))
{
continue;
}
var info = entityInfos[i];
for (var j = 0; j < info.componentCount; j++)
{
var comp = info.components[j];
if (!comp.typeID.IsValid || comp.entityFieldCount == 0)
{
continue;
}
var pComponent = world.EntityManager.GetComponent(entity, comp.typeID);
if (pComponent == null)
{
continue;
}
for (var f = 0; f < comp.entityFieldCount; f++)
{
var fieldOffset = comp.entityFieldOffsets[f];
var pField = (byte*)pComponent + fieldOffset;
var fileLocalIndex = *(int*)pField;
if (!forwardMap.TryGetValue(fileLocalIndex, out var remappedEntity))
{
remappedEntity = Entity.Invalid;
}
*(Entity*)pField = remappedEntity;
}
}
}
return Result.Success(header.entityCount);
}
} }
[StructLayout(LayoutKind.Sequential, Size = 64)] [StructLayout(LayoutKind.Sequential, Size = 64)]
@@ -225,7 +25,7 @@ internal struct SceneContentHeader
public int entityCount; public int entityCount;
} }
internal unsafe class SceneAssetEntry : ProcessableAssetEntry internal unsafe class SceneAssetEntry : AssetEntry
{ {
public class AdditionalData public class AdditionalData
{ {
@@ -244,11 +44,18 @@ internal unsafe class SceneAssetEntry : ProcessableAssetEntry
{ {
// TODO: How can I get this? Ideally the public api will be something like SceneManager.LoadScene(World, Scene, SceneLoadingType). // TODO: How can I get this? Ideally the public api will be something like SceneManager.LoadScene(World, Scene, SceneLoadingType).
// Should we handle the scene loading explicitly instead of auto loading on the first resolve? // Should we handle the scene loading explicitly instead of auto loading on the first resolve?
// For example if we have a component called SceneStreamer{ Scene a; Scene b; } // For example if we have a component called SceneStreamer{ SceneID a; SceneID b; }
// In save data, we convert the Scene(int) to a asset gui, and convert it back during load. So at ResolveScene stage (before the file even been loaded), we need to call the SceneManager.CreateScene(). // In save data, we convert the SceneID(ushort) to a asset gui, and convert it back during load. So at ResolveScene stage (before the file even been loaded), we need to call the SceneManager.CreateScene() and return the id immediately.
// Currently we store the world and loading type directly inside the asset entry, but actually that should not be bound with the asset itself, because we may load scene A along at the first time, then we load it additively at the second time. // Currently we store the world and loading type directly inside the asset entry, but actually that should not be bound with the asset itself, because we may load scene A along at the first time, then we load it additively at the second time.
// So, maybe the scene asset entry should only create a unique id from SceneManager.CreateScene() then resolve the scene file without loading it into world. // So, maybe the scene asset entry should only create a unique id from SceneManager.CreateScene() then resolve the scene file without loading it into world.
// Then we can load the scene into world using our job system, and user can decide to wait it immediatly (sync) or fire-and-forget (async). // Then we can load the scene into world using our job system, and user can decide to wait it immediatly (sync) or fire-and-forget (async).
// The workflow may be this:
// 1. Startup scene load, during resolve, see SceneStreamer has two SceneID fields (which still contains guid now), resolve this two scene via AssetManager. Get the id of those two scene immediately.
// 2. Background job load the scene into memory, parse the raw memory into the format that runtime understand. (Or maybe we do not load full memory, just check the header to see if it's valid?
// If the streamer type has 20 scenes, loading all 20 scenes into memory is very huge.).
// 3. The streamer called SceneManager.LoadScene(World, SceneID, SceneLoadingType) (example api, may not be this exactly). (Mybe we load the data into memory here every time when LoadScene is
// called? It will be fine right since load scene itself is a heavy opeartion and it's not am opeartion that will be performed per frame)
// 4. Background job load the scene into world by creating entities and setup components for those entities.
_targetWorld = World.GetWorld(0)!; _targetWorld = World.GetWorld(0)!;
_loadingType = SceneLoadingType.Single; _loadingType = SceneLoadingType.Single;
@@ -279,7 +86,7 @@ internal unsafe class SceneAssetEntry : ProcessableAssetEntry
return Result.Success(); return Result.Success();
} }
public override Result<JobHandle> OnProcessing(object? context) public Result<JobHandle> OnProcessing(object? context)
{ {
var pData = (byte*)_memory.GetUnsafePtr() + sizeof(SceneContentHeader); var pData = (byte*)_memory.GetUnsafePtr() + sizeof(SceneContentHeader);

View File

@@ -30,14 +30,14 @@ public class SceneGraphBuilderTests
private Entity CreateEntityWithScene(Scene scene) private Entity CreateEntityWithScene(Scene scene)
{ {
var entity = _world.EntityManager.CreateEntity(); var entity = _world.EntityManager.CreateEntity();
_world.EntityManager.AddComponent(entity, new SceneID { scene = scene }); _world.EntityManager.AddComponent(entity, new SceneID { value = scene });
return entity; return entity;
} }
private Entity CreateEntityWithSceneAndHierarchy(Scene scene, Entity parent) private Entity CreateEntityWithSceneAndHierarchy(Scene scene, Entity parent)
{ {
var entity = _world.EntityManager.CreateEntity(); var entity = _world.EntityManager.CreateEntity();
_world.EntityManager.AddComponent(entity, new SceneID { scene = scene }); _world.EntityManager.AddComponent(entity, new SceneID { value = scene });
_world.EntityManager.AddComponent(entity, new Hierarchy _world.EntityManager.AddComponent(entity, new Hierarchy
{ {
parent = Entity.Invalid, parent = Entity.Invalid,
@@ -165,7 +165,7 @@ public class SceneGraphBuilderTests
public void Build_InvalidSceneEntitiesAreExcluded() public void Build_InvalidSceneEntitiesAreExcluded()
{ {
var entity = _world.EntityManager.CreateEntity(); var entity = _world.EntityManager.CreateEntity();
_world.EntityManager.AddComponent(entity, new SceneID { scene = Scene.Invalid }); _world.EntityManager.AddComponent(entity, new SceneID { value = Scene.Invalid });
var nodes = SceneGraphBuilder.Build(_world); var nodes = SceneGraphBuilder.Build(_world);

View File

@@ -74,7 +74,7 @@ public class SceneSerializationTests
{ {
var world = _worldService.EditorWorld; var world = _worldService.EditorWorld;
var entity = world.EntityManager.CreateEntity(); var entity = world.EntityManager.CreateEntity();
world.EntityManager.AddComponent(entity, new SceneID { scene = scene }); world.EntityManager.AddComponent(entity, new SceneID { value = scene });
return entity; return entity;
} }
@@ -82,7 +82,7 @@ public class SceneSerializationTests
{ {
var world = _worldService.EditorWorld; var world = _worldService.EditorWorld;
var entity = world.EntityManager.CreateEntity(); var entity = world.EntityManager.CreateEntity();
world.EntityManager.AddComponent(entity, new SceneID { scene = scene }); world.EntityManager.AddComponent(entity, new SceneID { value = scene });
world.EntityManager.AddComponent(entity, Hierarchy.Root); world.EntityManager.AddComponent(entity, Hierarchy.Root);
world.EntityManager.AddComponent(entity, new LocalToWorld()); world.EntityManager.AddComponent(entity, new LocalToWorld());
@@ -129,7 +129,7 @@ public class SceneSerializationTests
using var scope = AllocationManager.CreateStackScope(); using var scope = AllocationManager.CreateStackScope();
using var entities = SceneManager.GetSceneEntities(scene, world, scope.AllocationHandle); using var entities = SceneManager.GetSceneEntities(scene, world, scope.AllocationHandle);
Assert.AreEqual(3, entities.Count, $"Expected 3 entities for scene {scene.id} but found {entities.Count}"); Assert.AreEqual(3, entities.Count, $"Expected 3 entities for scene {scene.ID} but found {entities.Count}");
} }
[TestMethod] [TestMethod]