Remove old project and continue improving ecs.
Updated packages version; Removed Ghost.SparseEntities; Added new EntityQuery.EntityComponentIterator; Added new thread local command buffer in World; Changed commands in EntityCommandBuffer from UnsafeList<Command> to UnsafeList<byte> for better performance; Changed the name of IJobEntityParallel to IJobEntity;
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.1" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.2.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.2.1" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.3.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.2.6" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.0" />
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
using Ghost.Test.Core;
|
||||
using Misaki.HighPerformance.Jobs;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
|
||||
namespace Ghost.Entities.Test;
|
||||
|
||||
internal struct TestEntityQueryJob : IJobEntityParallel<Transform>
|
||||
{
|
||||
public readonly void Execute(Entity entity, ref Transform transform)
|
||||
{
|
||||
transform.position += new float3(10, 10, 10);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ArcEntityTest : ITest
|
||||
{
|
||||
private JobScheduler _jobScheduler = null!;
|
||||
private World _world = null!;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
_jobScheduler = new JobScheduler(4);
|
||||
_world = World.Create(_jobScheduler);
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID<Transform>.value);
|
||||
_world.EntityCommandBuffer.AddComponent(entity1, new Mesh { index = 1 });
|
||||
|
||||
// var entity2 = _world.EntityManager.CreateEntity(ComponentTypeID<Transform>.value);
|
||||
// _world.EntityManager.SetComponentData(entity2, new Transform { position = new float3(1, 2, 3) });
|
||||
|
||||
Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent<Mesh>(entity1)}");
|
||||
|
||||
_world.EntityCommandBuffer.Playback();
|
||||
Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent<Mesh>(entity1)}");
|
||||
|
||||
_world.EntityCommandBuffer.RemoveComponent<Mesh>(entity1);
|
||||
Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent<Mesh>(entity1)}");
|
||||
|
||||
_world.EntityCommandBuffer.Playback();
|
||||
Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent<Mesh>(entity1)}");
|
||||
|
||||
// var queryID = new QueryBuilder().WithAll<Transform>().Build(_world);
|
||||
// ref var query = ref _world.GetEntityQueryReference(queryID);
|
||||
|
||||
// var testJob = new TestEntityQueryJob();
|
||||
// var handle = query.ScheduleEntityParallel<TestEntityQueryJob, Transform>(_jobScheduler, testJob, Allocator.Temp, 64, JobHandle.Invalid);
|
||||
// _jobScheduler.WaitComplete(handle);
|
||||
//
|
||||
// query.ForEach<Transform>((e, ref t) =>
|
||||
// {
|
||||
// Console.WriteLine($"Entity {e} Has Position: {t.position}");
|
||||
// });
|
||||
//
|
||||
// foreach (ref var transform in query.GetComponentIterator<Transform>())
|
||||
// {
|
||||
// Console.WriteLine($"Entity Updated Position: {transform.position}");
|
||||
// }
|
||||
//
|
||||
// foreach (var chunk in query.GetChunkIterator())
|
||||
// {
|
||||
// var transforms = chunk.GetComponentData<Transform>();
|
||||
// var entities = chunk.GetEntities();
|
||||
// var bits = chunk.GetEnableBits<Transform>();
|
||||
//
|
||||
// var it = bits.GetIterator();
|
||||
// while (it.Next(out var index) && index < chunk.Count)
|
||||
// {
|
||||
// Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
_world.Dispose();
|
||||
_jobScheduler.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public struct Transform : IEnableableComponent
|
||||
{
|
||||
public float3 position;
|
||||
}
|
||||
|
||||
public struct Mesh : IComponent
|
||||
{
|
||||
public int index;
|
||||
}
|
||||
@@ -1,193 +1,85 @@
|
||||
#if false
|
||||
using Ghost.SparseEntities.Components;
|
||||
using Ghost.SparseEntities.Query;
|
||||
using Ghost.SparseEntities.Systems;
|
||||
using Ghost.Test.Core;
|
||||
using System.Numerics;
|
||||
using Misaki.HighPerformance.Jobs;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Entities.Test;
|
||||
|
||||
internal struct TestEntityQueryJob : IJobEntity<Transform>
|
||||
{
|
||||
public readonly void Execute(Entity entity, ref Transform transform)
|
||||
{
|
||||
transform.position += new float3(10, 10, 10);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class EntityTest : ITest
|
||||
{
|
||||
private JobScheduler _jobScheduler = null!;
|
||||
private World _world = null!;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
_world = World.Create();
|
||||
_jobScheduler = new JobScheduler(4);
|
||||
_world = World.Create(_jobScheduler);
|
||||
|
||||
Console.WriteLine(Unsafe.SizeOf<EntityQuery>());
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var entity1 = _world.EntityManager.CreateEntity();
|
||||
var entity2 = _world.EntityManager.CreateEntity();
|
||||
var entity3 = _world.EntityManager.CreateEntity();
|
||||
var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID<Transform>.value);
|
||||
_world.EntityManager.AddComponent(entity1, new Mesh { index = 1 });
|
||||
|
||||
_world.EntityManager.AddComponent(entity1, new Transform { position = new Vector3(1, 2, 3) });
|
||||
_world.EntityManager.AddComponent(entity1, new Mesh { index = 42 });
|
||||
_world.EntityManager.AddScript<UIManager>(entity1);
|
||||
_world.EntityManager.AddScript<EventManager>(entity1);
|
||||
var entity2 = _world.EntityManager.CreateEntity(ComponentTypeID<Transform>.value);
|
||||
_world.EntityManager.SetComponent(entity2, new Transform { position = new float3(1, 2, 3) });
|
||||
|
||||
_world.EntityManager.AddComponent(entity2, new Transform { position = new Vector3(4, 5, 6) });
|
||||
_world.EntityManager.AddComponent(entity2, new Mesh { index = 43 });
|
||||
_world.EntityManager.AddScript<UserScript>(entity2);
|
||||
var queryID = new QueryBuilder().WithAll<Transform>().Build(_world);
|
||||
ref var query = ref _world.GetEntityQueryReference(queryID);
|
||||
|
||||
_world.EntityManager.AddComponent(entity3, new Transform { position = new Vector3(7, 8, 9) });
|
||||
_world.EntityManager.AddScript<EventManager>(entity3);
|
||||
var testJob = new TestEntityQueryJob();
|
||||
var handle = query.ScheduleEntityParallel<TestEntityQueryJob, Transform>(testJob, Allocator.Temp, 64, JobHandle.Invalid);
|
||||
_jobScheduler.WaitComplete(handle);
|
||||
|
||||
foreach (var (_, transform) in _world.Query<Transform>())
|
||||
query.ForEach<Transform>((e, ref t) =>
|
||||
{
|
||||
transform.ValueRW.position += new Vector3(1, 1, 1);
|
||||
Console.WriteLine($"Entity {e} Has Position: {t.position}");
|
||||
});
|
||||
|
||||
foreach (var (entity, transform) in query.GetEntityComponentIterator<Transform>())
|
||||
{
|
||||
Console.WriteLine($"Entity {entity} Updated Position: {transform.Get().position}");
|
||||
}
|
||||
|
||||
var filter = new QueryBuilder()
|
||||
.WithAll<Mesh>()
|
||||
.Build();
|
||||
|
||||
foreach (var (_, mesh) in _world.QueryFilter<Mesh>(in filter))
|
||||
foreach (var chunk in query.GetChunkIterator())
|
||||
{
|
||||
mesh.ValueRW.index += 1;
|
||||
var transforms = chunk.GetComponentData<Transform>();
|
||||
var entities = chunk.GetEntities();
|
||||
var bits = chunk.GetEnableBits<Transform>();
|
||||
|
||||
var it = bits.GetIterator();
|
||||
while (it.Next(out var index) && index < chunk.Count)
|
||||
{
|
||||
Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}");
|
||||
}
|
||||
}
|
||||
|
||||
_world.EntityManager.RemoveEntity(ref entity2);
|
||||
|
||||
var entity4 = _world.EntityManager.CreateEntity();
|
||||
_world.EntityManager.AddComponent(entity4, new Transform { position = new Vector3(10, 11, 12) });
|
||||
_world.EntityManager.AddComponent(entity4, new Mesh { index = 45 });
|
||||
_world.EntityManager.AddScript<UserScript>(entity4);
|
||||
|
||||
_world.SystemStorage.AddSystem<TestSystem2>();
|
||||
_world.SystemStorage.AddSystem<TestSystem>();
|
||||
|
||||
_world.SystemStorage.CreateSystems();
|
||||
_world.SystemStorage.UpdateSystems();
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
_world.Dispose();
|
||||
_jobScheduler.Dispose();
|
||||
JobScheduler.ReleaseTempAllocator();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestSystem : ISystem
|
||||
public struct Transform : IEnableableComponent
|
||||
{
|
||||
public void OnCreate(in SystemState state)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnUpdate(in SystemState state)
|
||||
{
|
||||
foreach (var (entity, transform) in state.World.Query<Transform>())
|
||||
{
|
||||
Console.WriteLine($"Entity {entity}: Transform Position = {transform.ValueRO.position}");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDestroy(in SystemState state)
|
||||
{
|
||||
}
|
||||
public float3 position;
|
||||
}
|
||||
|
||||
[DependsOn(typeof(TestSystem))]
|
||||
public class TestSystem2 : ISystem
|
||||
public struct Mesh : IComponent
|
||||
{
|
||||
public void OnCreate(in SystemState state)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnUpdate(in SystemState state)
|
||||
{
|
||||
foreach (var (entity, mesh) in state.World.Query<Mesh>())
|
||||
{
|
||||
Console.WriteLine($"Entity {entity}: Mesh Index = {mesh.ValueRO.index}");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDestroy(in SystemState state)
|
||||
{
|
||||
}
|
||||
public int index;
|
||||
}
|
||||
|
||||
public struct Transform : IComponentData
|
||||
{
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
public Vector3 scale;
|
||||
}
|
||||
|
||||
public struct Mesh : IComponentData
|
||||
{
|
||||
public uint index;
|
||||
}
|
||||
|
||||
public class UserScript : ScriptComponent
|
||||
{
|
||||
public override int ExecutionOrder => -1;
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
Console.WriteLine("UserScript enabled for entity: " + Owner);
|
||||
EntityManager.GetComponent<Transform>(Owner).ValueRW.position += new Vector3(10, 10, 10);
|
||||
}
|
||||
|
||||
public override void OnDisable()
|
||||
{
|
||||
Console.WriteLine("UserScript disabled for entity: " + Owner);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
Console.WriteLine("UserScript initialized for entity: " + Owner);
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
Console.WriteLine("UserScript started for entity: " + Owner);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
Console.WriteLine("UserScript updating for entity: " + Owner);
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
Console.WriteLine("UserScript destroyed for entity: " + Owner);
|
||||
}
|
||||
}
|
||||
|
||||
public class UIManager : ScriptComponent
|
||||
{
|
||||
public override void Start()
|
||||
{
|
||||
Console.WriteLine("UIManager started for entity: " + Owner);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
Console.WriteLine("UIManager updating for entity: " + Owner);
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
Console.WriteLine("UIManager destroyed for entity: " + Owner);
|
||||
}
|
||||
}
|
||||
|
||||
public class EventManager : ScriptComponent
|
||||
{
|
||||
public override void Start()
|
||||
{
|
||||
Console.WriteLine("EventManager started for entity: " + Owner);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
Console.WriteLine("EventManager updating for entity: " + Owner);
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
Console.WriteLine("EventManager destroyed for entity: " + Owner);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Entities\Ghost.Entities.csproj" />
|
||||
<ProjectReference Include="..\Ghost.SparseEntities\Ghost.SparseEntities.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Test.Core\Ghost.Test.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@ using Ghost.Test.Core;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
|
||||
AllocationManager.EnableDebugLayer();
|
||||
TestRunner.Run<ArcEntityTest>();
|
||||
TestRunner.Run<EntityTest>();
|
||||
AllocationManager.Dispose();
|
||||
@@ -2,37 +2,30 @@ using Ghost.Core;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
public unsafe class EntityCommandBuffer : IDisposable
|
||||
{
|
||||
private enum CommandType
|
||||
private enum ECBOpCode : byte
|
||||
{
|
||||
CreateEntity,
|
||||
CreateEntityWithComponents,
|
||||
DestroyEntity,
|
||||
AddComponent,
|
||||
RemoveComponent,
|
||||
SetComponent,
|
||||
}
|
||||
|
||||
private struct Command
|
||||
{
|
||||
public UnsafeArray<byte> data;
|
||||
public Entity entity;
|
||||
public CommandType type;
|
||||
public Identifier<IComponent> componentTypeID;
|
||||
}
|
||||
|
||||
private readonly EntityManager _entityManager;
|
||||
private UnsafeList<Command> _commands; // TODO: Maybe use UnsafeArray<byte> directly to save additional memory allocation in Unsafe<byte> data inside Command struct.
|
||||
private UnsafeList<byte> _buffer;
|
||||
private bool _disposed;
|
||||
|
||||
public EntityCommandBuffer(EntityManager entityManager)
|
||||
{
|
||||
_entityManager = entityManager;
|
||||
_commands = new UnsafeList<Command>(32, Allocator.Persistent);
|
||||
_buffer = new UnsafeList<byte>(4096, Allocator.Persistent);
|
||||
}
|
||||
|
||||
~EntityCommandBuffer()
|
||||
@@ -40,115 +33,165 @@ public unsafe class EntityCommandBuffer : IDisposable
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void WriteHeader(ECBOpCode op)
|
||||
{
|
||||
_buffer.Add((byte)op);
|
||||
}
|
||||
|
||||
private void Write<T>(T value)
|
||||
where T : unmanaged
|
||||
{
|
||||
var size = sizeof(T);
|
||||
var idx = _buffer.Count;
|
||||
|
||||
if (idx + size > _buffer.Capacity)
|
||||
{
|
||||
_buffer.Resize(idx + size);
|
||||
}
|
||||
|
||||
MemoryUtility.MemCpy((byte*)_buffer.GetUnsafePtr() + idx, &value, (nuint)size);
|
||||
}
|
||||
|
||||
private void WriteSpan<T>(ReadOnlySpan<T> span)
|
||||
where T : unmanaged
|
||||
{
|
||||
var size = sizeof(T) * span.Length;
|
||||
var idx = _buffer.Count;
|
||||
|
||||
if (idx + size > _buffer.Capacity)
|
||||
{
|
||||
_buffer.Resize(idx + size);
|
||||
}
|
||||
|
||||
fixed (T* ptr = span)
|
||||
{
|
||||
MemoryUtility.MemCpy((byte*)_buffer.GetUnsafePtr() + idx, ptr, (nuint)size);
|
||||
}
|
||||
}
|
||||
|
||||
private T Read<T>(ref int cursor)
|
||||
where T : unmanaged
|
||||
{
|
||||
var size = sizeof(T);
|
||||
var ptr = (byte*)_buffer.GetUnsafePtr();
|
||||
|
||||
var value = *(T*)&ptr[cursor];
|
||||
cursor += size;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private Span<T> ReadSpan<T>(ref int cursor, int length)
|
||||
where T : unmanaged
|
||||
{
|
||||
var size = sizeof(T) * length;
|
||||
var ptr = (byte*)_buffer.GetUnsafePtr();
|
||||
|
||||
var span = new Span<T>(&ptr[cursor], length);
|
||||
cursor += size;
|
||||
|
||||
return span;
|
||||
}
|
||||
|
||||
private void* ReadBuffer(ref int cursor, int size)
|
||||
{
|
||||
var ptr = (byte*)_buffer.GetUnsafePtr();
|
||||
var bufferPtr = ptr + cursor;
|
||||
cursor += size;
|
||||
|
||||
return bufferPtr;
|
||||
}
|
||||
|
||||
public void CreateEntity()
|
||||
{
|
||||
var command = new Command
|
||||
{
|
||||
type = CommandType.CreateEntity,
|
||||
data = default,
|
||||
entity = default,
|
||||
componentTypeID = -1
|
||||
};
|
||||
|
||||
_commands.Add(command);
|
||||
WriteHeader(ECBOpCode.CreateEntity);
|
||||
}
|
||||
|
||||
public void CreateEntity(params ReadOnlySpan<Identifier<IComponent>> componentTypeIDs)
|
||||
{
|
||||
var data = new UnsafeArray<byte>(componentTypeIDs.Length * sizeof(int), Allocator.Temp);
|
||||
MemoryMarshal.Cast<Identifier<IComponent>, byte>(componentTypeIDs).CopyTo(data.AsSpan());
|
||||
|
||||
var command = new Command
|
||||
{
|
||||
type = CommandType.CreateEntity,
|
||||
data = data,
|
||||
entity = Entity.Invalid,
|
||||
componentTypeID = Identifier<IComponent>.Invalid
|
||||
};
|
||||
|
||||
_commands.Add(command);
|
||||
WriteHeader(ECBOpCode.CreateEntityWithComponents);
|
||||
Write(componentTypeIDs.Length);
|
||||
WriteSpan(componentTypeIDs);
|
||||
}
|
||||
|
||||
public void DestroyEntity(Entity entity)
|
||||
{
|
||||
_commands.Add(new Command
|
||||
{
|
||||
type = CommandType.DestroyEntity,
|
||||
data = default,
|
||||
entity = entity,
|
||||
componentTypeID = Identifier<IComponent>.Invalid
|
||||
});
|
||||
WriteHeader(ECBOpCode.DestroyEntity);
|
||||
Write(entity);
|
||||
}
|
||||
|
||||
public void AddComponent<T>(Entity entity, T component = default)
|
||||
where T : unmanaged, IComponent
|
||||
{
|
||||
var data = new UnsafeArray<byte>(sizeof(T), Allocator.Temp);
|
||||
MemoryUtility.MemCpy(data.GetUnsafePtr(), &component, (nuint)sizeof(T));
|
||||
|
||||
_commands.Add(new Command
|
||||
{
|
||||
type = CommandType.AddComponent,
|
||||
data = data,
|
||||
entity = entity,
|
||||
componentTypeID = ComponentTypeID<T>.value
|
||||
});
|
||||
WriteHeader(ECBOpCode.AddComponent);
|
||||
Write(entity);
|
||||
Write(ComponentTypeID<T>.value);
|
||||
Write(component);
|
||||
}
|
||||
|
||||
public void RemoveComponent<T>(Entity entity)
|
||||
where T : unmanaged, IComponent
|
||||
{
|
||||
_commands.Add(new Command
|
||||
{
|
||||
type = CommandType.RemoveComponent,
|
||||
data = default,
|
||||
entity = entity,
|
||||
componentTypeID = ComponentTypeID<T>.value
|
||||
});
|
||||
WriteHeader(ECBOpCode.RemoveComponent);
|
||||
Write(entity);
|
||||
Write(ComponentTypeID<T>.value);
|
||||
}
|
||||
|
||||
public void SetComponent<T>(Entity entity, T component)
|
||||
where T : unmanaged, IComponent
|
||||
{
|
||||
var data = new UnsafeArray<byte>(sizeof(T), Allocator.Temp);
|
||||
MemoryUtility.MemCpy(data.GetUnsafePtr(), &component, (nuint)sizeof(T));
|
||||
|
||||
_commands.Add(new Command
|
||||
{
|
||||
type = CommandType.SetComponent,
|
||||
data = data,
|
||||
entity = entity,
|
||||
componentTypeID = ComponentTypeID<T>.value
|
||||
});
|
||||
WriteHeader(ECBOpCode.SetComponent);
|
||||
Write(entity);
|
||||
Write(ComponentTypeID<T>.value);
|
||||
Write(component);
|
||||
}
|
||||
|
||||
internal void Playback()
|
||||
{
|
||||
foreach (ref var command in _commands)
|
||||
var cursor = 0;
|
||||
var length = _buffer.Count;
|
||||
var ptr = (byte*)_buffer.GetUnsafePtr();
|
||||
|
||||
while (cursor < length)
|
||||
{
|
||||
switch (command.type)
|
||||
{
|
||||
case CommandType.CreateEntity:
|
||||
if (command.data.Count > 0)
|
||||
{
|
||||
_entityManager.CreateEntity(MemoryMarshal.Cast<byte, Identifier<IComponent>>(command.data.AsSpan()));
|
||||
}
|
||||
else
|
||||
var op = *(ECBOpCode*)ptr[cursor];
|
||||
cursor += sizeof(ECBOpCode);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case ECBOpCode.CreateEntity:
|
||||
_entityManager.CreateEntity();
|
||||
}
|
||||
break;
|
||||
case CommandType.DestroyEntity:
|
||||
_entityManager.DestroyEntity(command.entity);
|
||||
|
||||
case ECBOpCode.CreateEntityWithComponents:
|
||||
var compCount = Read<int>(ref cursor);
|
||||
var compTypeIDs = ReadSpan<Identifier<IComponent>>(ref cursor, compCount);
|
||||
_entityManager.CreateEntity(compTypeIDs);
|
||||
break;
|
||||
case CommandType.AddComponent:
|
||||
_entityManager.AddComponent(command.entity, command.componentTypeID, command.data.GetUnsafePtr());
|
||||
|
||||
case ECBOpCode.DestroyEntity:
|
||||
var entityToDestroy = Read<Entity>(ref cursor);
|
||||
_entityManager.DestroyEntity(entityToDestroy);
|
||||
break;
|
||||
case CommandType.RemoveComponent:
|
||||
_entityManager.RemoveComponent(command.entity, command.componentTypeID);
|
||||
|
||||
case ECBOpCode.AddComponent:
|
||||
var entityToAdd = Read<Entity>(ref cursor);
|
||||
var addCompTypeID = Read<Identifier<IComponent>>(ref cursor);
|
||||
var pAddCompData = ReadBuffer(ref cursor, ComponentRegister.GetComponentInfo(addCompTypeID).size);
|
||||
_entityManager.AddComponent(entityToAdd, addCompTypeID, pAddCompData);
|
||||
break;
|
||||
case CommandType.SetComponent:
|
||||
_entityManager.SetComponent(command.entity, command.componentTypeID, command.data.GetUnsafePtr());
|
||||
|
||||
case ECBOpCode.RemoveComponent:
|
||||
var entityToRemove = Read<Entity>(ref cursor);
|
||||
var removeCompTypeID = Read<Identifier<IComponent>>(ref cursor);
|
||||
_entityManager.RemoveComponent(entityToRemove, removeCompTypeID);
|
||||
break;
|
||||
|
||||
case ECBOpCode.SetComponent:
|
||||
var entityToSet = Read<Entity>(ref cursor);
|
||||
var setCompTypeID = Read<Identifier<IComponent>>(ref cursor);
|
||||
var pSetCompData = ReadBuffer(ref cursor, ComponentRegister.GetComponentInfo(setCompTypeID).size);
|
||||
_entityManager.SetComponent(entityToSet, setCompTypeID, pSetCompData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -156,14 +199,10 @@ public unsafe class EntityCommandBuffer : IDisposable
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void Reset()
|
||||
{
|
||||
foreach (ref var command in _commands)
|
||||
{
|
||||
command.data.Dispose();
|
||||
}
|
||||
|
||||
_commands.Clear();
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -173,12 +212,7 @@ public unsafe class EntityCommandBuffer : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ref var command in _commands)
|
||||
{
|
||||
command.data.Dispose();
|
||||
}
|
||||
|
||||
_commands.Dispose();
|
||||
_buffer.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
@@ -19,9 +19,7 @@ public readonly ref struct SystemAPI
|
||||
public interface ISystem
|
||||
{
|
||||
void Initialize(ref readonly SystemAPI systemAPI);
|
||||
void PreUpdate(ref readonly SystemAPI systemAPI);
|
||||
void Update(ref readonly SystemAPI systemAPI);
|
||||
void PostUpdate(ref readonly SystemAPI systemAPI);
|
||||
void Cleanup(ref readonly SystemAPI systemAPI);
|
||||
}
|
||||
|
||||
@@ -207,16 +205,6 @@ public abstract class SystemGroup : ISystem
|
||||
}
|
||||
}
|
||||
|
||||
public void PreUpdate(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
ThrowIfNotSorted();
|
||||
|
||||
foreach (var system in _sortedSystems!)
|
||||
{
|
||||
system.PreUpdate(in systemAPI);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
ThrowIfNotSorted();
|
||||
@@ -227,16 +215,6 @@ public abstract class SystemGroup : ISystem
|
||||
}
|
||||
}
|
||||
|
||||
public void PostUpdate(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
ThrowIfNotSorted();
|
||||
|
||||
foreach (var system in _sortedSystems!)
|
||||
{
|
||||
system.PostUpdate(in systemAPI);
|
||||
}
|
||||
}
|
||||
|
||||
public void Cleanup(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
ThrowIfNotSorted();
|
||||
@@ -282,4 +260,28 @@ public class SystemManager
|
||||
|
||||
throw new InvalidOperationException($"System of type {typeof(T).FullName} not found in SystemManager.");
|
||||
}
|
||||
|
||||
internal void InitializeAll(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
foreach (var system in _systems)
|
||||
{
|
||||
system.Initialize(in systemAPI);
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateAll(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
foreach (var system in _systems)
|
||||
{
|
||||
system.Update(in systemAPI);
|
||||
}
|
||||
}
|
||||
|
||||
internal void CleanupAll(ref readonly SystemAPI systemAPI)
|
||||
{
|
||||
foreach (var system in _systems)
|
||||
{
|
||||
system.Cleanup(in systemAPI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1610
Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.gen.cs
Normal file
1610
Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.gen.cs
Normal file
File diff suppressed because it is too large
Load Diff
199
Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.tt
Normal file
199
Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.tt
Normal file
@@ -0,0 +1,199 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ output extension="gen.cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ include file="Helpers.ttinclude" #>
|
||||
using Ghost.Core;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
public unsafe partial struct EntityQuery
|
||||
{
|
||||
<# for (var i = 1; i <= Amount; i++)
|
||||
{
|
||||
var generics = AppendParameters(i, "T{0}");
|
||||
var compGenerics = AppendParameters(i, "ref T{0} component{0}");
|
||||
var deconstrictOutPrams = AppendParameters(i, "out Ref<T{0}> component{0}");
|
||||
var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 2);
|
||||
#>
|
||||
public readonly ref struct EntityComponentIterator<<#= generics #>>
|
||||
<#= restrictions #>
|
||||
{
|
||||
public ref struct QueryItem
|
||||
{
|
||||
public Entity entity;
|
||||
|
||||
<# for (var j = 0; j < i; j++) { #>
|
||||
public ref T<#= j #> component<#= j #>;
|
||||
<# } #>
|
||||
internal QueryItem(Entity entity, <#= compGenerics #>)
|
||||
{
|
||||
this.entity = entity;
|
||||
|
||||
<# for (var j = 0; j < i; j++) { #>
|
||||
this.component<#= j #> = ref component<#= j #>;
|
||||
<# } #>
|
||||
}
|
||||
|
||||
public void Deconstruct(out Entity entity, <#= deconstrictOutPrams #>)
|
||||
{
|
||||
entity = this.entity;
|
||||
|
||||
<# for (var j = 0; j < i; j++) { #>
|
||||
component<#= j #> = new Ref<T<#= j #>>(ref this.component<#= j #>);
|
||||
<# } #>
|
||||
}
|
||||
}
|
||||
|
||||
public ref struct Enumerator
|
||||
{
|
||||
private fixed int _compTypeIDs[<#= i #>];
|
||||
private fixed int _offsets[<#= i #>];
|
||||
private fixed long _compBasePtrs[<#= i #>];
|
||||
|
||||
private readonly ReadOnlyUnsafeCollection<Identifier<Archetype>> _matchingArchetypes;
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
private ref Archetype _currentArchetype;
|
||||
private ref Chunk _currentChunk;
|
||||
private byte* _chunkBasePtr;
|
||||
|
||||
private int _currentChunkEntityCount;
|
||||
private int _currentArchetypeIndex;
|
||||
private int _currentChunkIndex;
|
||||
private int _currentEntityIndex;
|
||||
|
||||
internal Enumerator(ReadOnlyUnsafeCollection<Identifier<Archetype>> matchingArchetypes, EntityQueryMask mask, World world)
|
||||
{
|
||||
<# for (var j = 0; j < i; j++) { #>
|
||||
_compTypeIDs[<#= j #>] = ComponentTypeID<T<#= j #>>.value;
|
||||
_offsets[<#= j #>] = 0;
|
||||
_compBasePtrs[<#= j #>] = 0;
|
||||
|
||||
<# } #>
|
||||
_matchingArchetypes = matchingArchetypes;
|
||||
_mask = mask;
|
||||
_world = world;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
public QueryItem Current => new(
|
||||
*(Entity*)(_chunkBasePtr + _currentArchetype.EntityIDsOffset + _currentEntityIndex * sizeof(Entity)),
|
||||
<# for (var j = 0; j < i; j++) { #>
|
||||
ref *(T<#= j #>*)(_compBasePtrs[<#= j #>] + _currentEntityIndex * sizeof(T<#= j #>))<#= j < i - 1 ? "," : "" #>
|
||||
<# } #>
|
||||
);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SetChunk(int chunkIndex)
|
||||
{
|
||||
_currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex);
|
||||
_chunkBasePtr = _currentChunk.GetUnsafePtr();
|
||||
_currentChunkEntityCount = _currentChunk.Count;
|
||||
|
||||
for (var index = 0; index < <#= i #>; index++)
|
||||
{
|
||||
var layout = _currentArchetype.GetLayout(_compTypeIDs[index])
|
||||
.GetValueOrThrow(ResultStatus.Success);
|
||||
_offsets[index] = layout.offset;
|
||||
_compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_currentEntityIndex++;
|
||||
if (_currentEntityIndex < _currentChunk.Count)
|
||||
{
|
||||
var pChunkData = _currentChunk.GetUnsafePtr();
|
||||
if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
_currentChunkIndex++;
|
||||
if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount)
|
||||
{
|
||||
SetChunk(_currentChunkIndex);
|
||||
_currentEntityIndex = -1; // Reset for new chunk
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
_currentArchetypeIndex++;
|
||||
if (_currentArchetypeIndex < _matchingArchetypes.Count)
|
||||
{
|
||||
_currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]);
|
||||
|
||||
_currentChunkIndex = 0;
|
||||
if (_currentArchetype.ChunkCount > 0)
|
||||
{
|
||||
SetChunk(0);
|
||||
_currentEntityIndex = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If archetype has no chunks, loop will try next archetype
|
||||
}
|
||||
else
|
||||
{
|
||||
return false; // End of all data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_currentArchetype = ref Unsafe.NullRef<Archetype>();
|
||||
_currentChunk = ref Unsafe.NullRef<Chunk>();
|
||||
_currentArchetypeIndex = 0;
|
||||
_currentChunkIndex = 0;
|
||||
_currentEntityIndex = -1;
|
||||
|
||||
if (_matchingArchetypes.Count > 0)
|
||||
{
|
||||
_currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]);
|
||||
if (_currentArchetype.ChunkCount > 0)
|
||||
{
|
||||
SetChunk(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ReadOnlyUnsafeCollection<Identifier<Archetype>> _matchingArchetypes;
|
||||
private readonly EntityQueryMask _mask;
|
||||
private readonly World _world;
|
||||
|
||||
internal EntityComponentIterator(ReadOnlyUnsafeCollection<Identifier<Archetype>> matchingArchetypes, EntityQueryMask mask, World world)
|
||||
{
|
||||
_matchingArchetypes = matchingArchetypes;
|
||||
_mask = mask;
|
||||
_world = world;
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(_matchingArchetypes, _mask, _world);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly EntityComponentIterator<<#= generics#>> GetEntityComponentIterator<<#= generics#>>()
|
||||
<#= restrictions #>
|
||||
{
|
||||
return new EntityComponentIterator<<#= generics#>>(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success));
|
||||
}
|
||||
|
||||
<# } #>
|
||||
}
|
||||
@@ -5,14 +5,14 @@ using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
public interface IJobEntityParallel<T0>
|
||||
public interface IJobEntity<T0>
|
||||
where T0 : unmanaged, IComponent
|
||||
{
|
||||
void Execute(Entity entity, ref T0 component0);
|
||||
}
|
||||
|
||||
internal unsafe struct JobEntityBatch<TJob, T0> : IJobParallelFor
|
||||
where TJob : unmanaged, IJobEntityParallel<T0>
|
||||
where TJob : unmanaged, IJobEntity<T0>
|
||||
where T0 : unmanaged, IComponent
|
||||
{
|
||||
public TJob userJob;
|
||||
@@ -48,7 +48,7 @@ internal unsafe struct JobEntityBatch<TJob, T0> : IJobParallelFor
|
||||
}
|
||||
}
|
||||
|
||||
public interface IJobEntityParallel<T0, T1>
|
||||
public interface IJobEntity<T0, T1>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
{
|
||||
@@ -56,7 +56,7 @@ public interface IJobEntityParallel<T0, T1>
|
||||
}
|
||||
|
||||
internal unsafe struct JobEntityBatch<TJob, T0, T1> : IJobParallelFor
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
{
|
||||
@@ -105,7 +105,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1> : IJobParallelFor
|
||||
}
|
||||
}
|
||||
|
||||
public interface IJobEntityParallel<T0, T1, T2>
|
||||
public interface IJobEntity<T0, T1, T2>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -114,7 +114,7 @@ public interface IJobEntityParallel<T0, T1, T2>
|
||||
}
|
||||
|
||||
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2> : IJobParallelFor
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -176,7 +176,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2> : IJobParallelFor
|
||||
}
|
||||
}
|
||||
|
||||
public interface IJobEntityParallel<T0, T1, T2, T3>
|
||||
public interface IJobEntity<T0, T1, T2, T3>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -186,7 +186,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3>
|
||||
}
|
||||
|
||||
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3> : IJobParallelFor
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -261,7 +261,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3> : IJobParallelFor
|
||||
}
|
||||
}
|
||||
|
||||
public interface IJobEntityParallel<T0, T1, T2, T3, T4>
|
||||
public interface IJobEntity<T0, T1, T2, T3, T4>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -272,7 +272,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3, T4>
|
||||
}
|
||||
|
||||
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4> : IJobParallelFor
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -360,7 +360,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4> : IJobParallelFo
|
||||
}
|
||||
}
|
||||
|
||||
public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5>
|
||||
public interface IJobEntity<T0, T1, T2, T3, T4, T5>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -372,7 +372,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5>
|
||||
}
|
||||
|
||||
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5> : IJobParallelFor
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -473,7 +473,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5> : IJobParall
|
||||
}
|
||||
}
|
||||
|
||||
public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6>
|
||||
public interface IJobEntity<T0, T1, T2, T3, T4, T5, T6>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -486,7 +486,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6>
|
||||
}
|
||||
|
||||
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5, T6> : IJobParallelFor
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5, T6>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -600,7 +600,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5, T6> : IJobPa
|
||||
}
|
||||
}
|
||||
|
||||
public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
public interface IJobEntity<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -614,7 +614,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
}
|
||||
|
||||
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5, T6, T7> : IJobParallelFor
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -765,7 +765,7 @@ public unsafe partial struct EntityQuery
|
||||
}
|
||||
|
||||
public JobHandle ScheduleEntityParallel<TJob, T0>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
|
||||
where TJob : unmanaged, IJobEntityParallel<T0>
|
||||
where TJob : unmanaged, IJobEntity<T0>
|
||||
where T0 : unmanaged, IComponent
|
||||
{
|
||||
var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success);
|
||||
@@ -867,7 +867,7 @@ public unsafe partial struct EntityQuery
|
||||
}
|
||||
|
||||
public JobHandle ScheduleEntityParallel<TJob, T0, T1>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
{
|
||||
@@ -990,7 +990,7 @@ public unsafe partial struct EntityQuery
|
||||
}
|
||||
|
||||
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -1134,7 +1134,7 @@ public unsafe partial struct EntityQuery
|
||||
}
|
||||
|
||||
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -1299,7 +1299,7 @@ public unsafe partial struct EntityQuery
|
||||
}
|
||||
|
||||
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3, T4>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -1485,7 +1485,7 @@ public unsafe partial struct EntityQuery
|
||||
}
|
||||
|
||||
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3, T4, T5>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -1692,7 +1692,7 @@ public unsafe partial struct EntityQuery
|
||||
}
|
||||
|
||||
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3, T4, T5, T6>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5, T6>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -1920,7 +1920,7 @@ public unsafe partial struct EntityQuery
|
||||
}
|
||||
|
||||
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3, T4, T5, T6, T7>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
|
||||
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
where T0 : unmanaged, IComponent
|
||||
where T1 : unmanaged, IComponent
|
||||
where T2 : unmanaged, IComponent
|
||||
@@ -16,14 +16,14 @@ namespace Ghost.Entities;
|
||||
var generics = AppendGenerics(i);
|
||||
var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 1);
|
||||
#>
|
||||
public interface IJobEntityParallel<<#= generics #>>
|
||||
public interface IJobEntity<<#= generics #>>
|
||||
<#= restrictions #>
|
||||
{
|
||||
void Execute(Entity entity, <#= AppendParameters(i, "ref T{0} component{0}") #>);
|
||||
}
|
||||
|
||||
internal unsafe struct JobEntityBatch<TJob, <#= generics #>> : IJobParallelFor
|
||||
where TJob : unmanaged, IJobEntityParallel<<#= generics #>>
|
||||
where TJob : unmanaged, IJobEntity<<#= generics #>>
|
||||
<#= restrictions #>
|
||||
{
|
||||
public TJob userJob;
|
||||
@@ -101,7 +101,7 @@ public unsafe partial struct EntityQuery
|
||||
}
|
||||
|
||||
public JobHandle ScheduleEntityParallel<TJob, <#= generics #>>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
|
||||
where TJob : unmanaged, IJobEntityParallel<<#= generics #>>
|
||||
where TJob : unmanaged, IJobEntity<<#= generics #>>
|
||||
<#= restrictions #>
|
||||
{
|
||||
var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success);
|
||||
@@ -72,6 +72,7 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
||||
|
||||
private readonly EntityManager _entityManager;
|
||||
private readonly EntityCommandBuffer _entityCommandBuffer;
|
||||
private readonly EntityCommandBuffer[] _threadLocalECBs;
|
||||
|
||||
private UnsafeList<Archetype> _archetypes;
|
||||
private UnsafeList<EntityQuery> _entityQueries;
|
||||
@@ -83,9 +84,24 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
||||
|
||||
internal int ArchetypeCount => _archetypes.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of this world.
|
||||
/// </summary>
|
||||
public Identifier<World> ID => _id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the job scheduler associated with this world.
|
||||
/// </summary>
|
||||
public JobScheduler JobScheduler => _jobScheduler;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the publicntity manager for this world.
|
||||
/// </summary>
|
||||
public EntityManager EntityManager => _entityManager;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the main entity command buffer for this world.
|
||||
/// </summary>
|
||||
public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer;
|
||||
|
||||
private World(Identifier<World> id, int entityCapacity, JobScheduler jobScheduler)
|
||||
@@ -101,6 +117,12 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
||||
|
||||
_entityManager = new EntityManager(this, entityCapacity);
|
||||
_entityCommandBuffer = new EntityCommandBuffer(_entityManager);
|
||||
_threadLocalECBs = new EntityCommandBuffer[jobScheduler.WorkerCount];
|
||||
|
||||
for (var i = 0; i < jobScheduler.WorkerCount; i++)
|
||||
{
|
||||
_threadLocalECBs[i] = new EntityCommandBuffer(_entityManager);
|
||||
}
|
||||
|
||||
// Create the empty archetype
|
||||
CreateArchetype(ReadOnlySpan<Identifier<IComponent>>.Empty, 0);
|
||||
@@ -171,12 +193,34 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
||||
return Identifier<EntityQuery>.Invalid;
|
||||
}
|
||||
|
||||
internal void PlaybackEntityCommandBuffers()
|
||||
{
|
||||
_entityCommandBuffer.Playback();
|
||||
|
||||
for (var i = 0; i < _threadLocalECBs.Length; i++)
|
||||
{
|
||||
_threadLocalECBs[i].Playback();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the entity query with the specified identifier.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref EntityQuery GetEntityQueryReference(Identifier<EntityQuery> id)
|
||||
{
|
||||
return ref _entityQueries[id.value];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the thread-local entity command buffer for the specified thread index.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityCommandBuffer GetThreadLocalEntityCommandBuffer(int threadIndex)
|
||||
{
|
||||
return _threadLocalECBs[threadIndex];
|
||||
}
|
||||
|
||||
public bool Equals(World? other)
|
||||
{
|
||||
return other is not null && _id == other._id;
|
||||
@@ -221,6 +265,10 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
||||
|
||||
_entityManager.Dispose();
|
||||
_entityCommandBuffer.Dispose();
|
||||
for (var i = 0; i < _threadLocalECBs.Length; i++)
|
||||
{
|
||||
_threadLocalECBs[i].Dispose();
|
||||
}
|
||||
|
||||
_archetypes.Dispose();
|
||||
_entityQueries.Dispose();
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
global using EntityID = System.Int32;
|
||||
global using GenerationID = System.UInt16;
|
||||
global using WorldID = System.UInt16;
|
||||
|
||||
using Ghost.Core.Attributes;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Ghost.Engine")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Entities.Test")]
|
||||
|
||||
[assembly: EngineAssembly]
|
||||
@@ -1,715 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.SparseEntities.Components;
|
||||
|
||||
internal static class SingletonContainer<T>
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
public static readonly Dictionary<int, T> container = new();
|
||||
}
|
||||
|
||||
internal interface IComponentPool : IDisposable
|
||||
{
|
||||
public EntityID Count
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public void Add(Entity entity, IComponentData component);
|
||||
public bool Remove(Entity entity);
|
||||
public bool Has(Entity entity);
|
||||
public IComponentData Get(Entity entity);
|
||||
public IntPtr GetUnsafe(Entity entity);
|
||||
public void Set(Entity entity, in IComponentData component);
|
||||
|
||||
public IEnumerable<(Entity entity, IComponentData component)> Enumerate();
|
||||
}
|
||||
|
||||
internal interface IComponentPool<T> : IComponentPool
|
||||
where T : IComponentData
|
||||
{
|
||||
public void Add(Entity entity, T Component);
|
||||
public void Set(Entity entity, in T component);
|
||||
}
|
||||
|
||||
internal class ComponentPool<T> : IComponentPool<T>
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
private struct ComponentMetadata
|
||||
{
|
||||
public T data;
|
||||
public Entity owner;
|
||||
}
|
||||
|
||||
private EntityID _nextId;
|
||||
private EntityID _capacity;
|
||||
|
||||
private ComponentMetadata[] _components;
|
||||
private EntityID[] _lookup;
|
||||
|
||||
public EntityID Count => _nextId;
|
||||
|
||||
public ComponentPool(int initialSize)
|
||||
{
|
||||
_nextId = 0;
|
||||
_capacity = initialSize;
|
||||
|
||||
_components = new ComponentMetadata[initialSize];
|
||||
_lookup = new EntityID[initialSize];
|
||||
|
||||
_lookup.AsSpan().Fill(Entity.INVALID_ID);
|
||||
}
|
||||
|
||||
public ComponentPool()
|
||||
: this(16)
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static EntityID GetLookupIndex(Entity entity)
|
||||
{
|
||||
return entity.ID;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private EntityID GetComponentIndex(Entity entity)
|
||||
{
|
||||
return _lookup[GetLookupIndex(entity)];
|
||||
}
|
||||
|
||||
public void Add(Entity entity, IComponentData component)
|
||||
{
|
||||
if (component is not T typedComponent)
|
||||
{
|
||||
throw new ArgumentException($"Component type mismatch. Expected {typeof(T)}, but got {component.GetType()}.");
|
||||
}
|
||||
|
||||
Add(entity, typedComponent);
|
||||
}
|
||||
|
||||
public void Add(Entity entity, T component)
|
||||
{
|
||||
if (!entity.IsValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var lookupIndex = GetLookupIndex(entity);
|
||||
var componentIndex = GetComponentIndex(entity);
|
||||
|
||||
if (componentIndex != Entity.INVALID_ID)
|
||||
{
|
||||
// Overwrite the old data if generation is larger
|
||||
if (entity.Generation > _components[componentIndex].owner.Generation)
|
||||
{
|
||||
var index = _lookup[lookupIndex];
|
||||
_components[index].data = component;
|
||||
_components[index].owner = entity;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_nextId >= _capacity)
|
||||
{
|
||||
var newCapacity = _capacity * 2;
|
||||
Array.Resize(ref _components, newCapacity);
|
||||
Array.Resize(ref _lookup, newCapacity);
|
||||
_lookup.AsSpan(_capacity, newCapacity - _capacity).Fill(Entity.INVALID_ID);
|
||||
|
||||
_capacity = newCapacity;
|
||||
}
|
||||
|
||||
_lookup[lookupIndex] = _nextId;
|
||||
_components[_nextId] = new ComponentMetadata
|
||||
{
|
||||
data = component,
|
||||
owner = entity
|
||||
};
|
||||
|
||||
_nextId++;
|
||||
}
|
||||
|
||||
public bool Remove(Entity entity)
|
||||
{
|
||||
// We do not remove anything here, the generation of the entity will be used to determine if the component is valid.
|
||||
return true;
|
||||
}
|
||||
|
||||
public IComponentData Get(Entity entity)
|
||||
{
|
||||
return GetRef(entity);
|
||||
}
|
||||
|
||||
public unsafe IntPtr GetUnsafe(Entity entity)
|
||||
{
|
||||
return (IntPtr)Unsafe.AsPointer(ref GetRef(entity));
|
||||
}
|
||||
|
||||
public ref T GetRef(Entity entity)
|
||||
{
|
||||
if (!entity.IsValid)
|
||||
{
|
||||
return ref Unsafe.NullRef<T>();
|
||||
}
|
||||
|
||||
var index = GetComponentIndex(entity);
|
||||
return ref _components[index].data;
|
||||
}
|
||||
|
||||
public bool Has(Entity entity)
|
||||
{
|
||||
if (entity.ID >= _lookup.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var index = GetComponentIndex(entity);
|
||||
return index != Entity.INVALID_ID && _components[index].owner.Generation == entity.Generation;
|
||||
}
|
||||
|
||||
public void Set(Entity entity, in IComponentData component)
|
||||
{
|
||||
if (component is not T typedComponent)
|
||||
{
|
||||
throw new ArgumentException($"Component type mismatch. Expected {typeof(T)}, but got {component.GetType()}.");
|
||||
}
|
||||
Set(entity, typedComponent);
|
||||
}
|
||||
|
||||
public void Set(Entity entity, in T component)
|
||||
{
|
||||
if (!entity.IsValid || entity.ID >= _lookup.Length || GetComponentIndex(entity) == Entity.INVALID_ID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var index = GetComponentIndex(entity);
|
||||
_components[index].data = component;
|
||||
_components[index].owner = entity;
|
||||
}
|
||||
|
||||
public IEnumerable<(Entity entity, IComponentData component)> Enumerate()
|
||||
{
|
||||
for (var i = 0; i < _nextId; i++)
|
||||
{
|
||||
if (_components[i].owner.IsValid)
|
||||
{
|
||||
yield return (_components[i].owner, _components[i].data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_components = Array.Empty<ComponentMetadata>();
|
||||
_lookup = Array.Empty<EntityID>();
|
||||
_nextId = 0;
|
||||
_capacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ScriptComponentPool : IComponentPool<ScriptComponent>
|
||||
{
|
||||
private Dictionary<Entity, List<ScriptComponent>>? _scriptComponents;
|
||||
private List<ScriptComponent>? _executionList;
|
||||
|
||||
private readonly World _world;
|
||||
|
||||
internal IReadOnlyDictionary<Entity, List<ScriptComponent>>? ScriptComponents => _scriptComponents;
|
||||
internal IReadOnlyList<ScriptComponent>? ExecutionList => _executionList;
|
||||
|
||||
public bool IsInitialized => _scriptComponents != null;
|
||||
public int Count => _scriptComponents?.Keys.Count ?? 0;
|
||||
|
||||
public ScriptComponentPool(World world)
|
||||
{
|
||||
_world = world;
|
||||
}
|
||||
|
||||
internal void Initialize(int capacity = 16)
|
||||
{
|
||||
_scriptComponents ??= new(capacity);
|
||||
}
|
||||
|
||||
internal void RebuildExecutionList()
|
||||
{
|
||||
if (_scriptComponents == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_executionList ??= new List<ScriptComponent>(_scriptComponents.Count);
|
||||
_executionList.Clear();
|
||||
|
||||
foreach (var kvp in _scriptComponents)
|
||||
{
|
||||
_executionList.AddRange(kvp.Value);
|
||||
}
|
||||
|
||||
_executionList.Sort((a, b) => a.ExecutionOrder.CompareTo(b.ExecutionOrder));
|
||||
}
|
||||
|
||||
public void Add(Entity entity, IComponentData component)
|
||||
{
|
||||
if (component is not ScriptComponent scriptComponent)
|
||||
{
|
||||
throw new ArgumentException($"Component type mismatch. Expected {typeof(ScriptComponent)}, but got {component.GetType()}.");
|
||||
}
|
||||
|
||||
Add(entity, scriptComponent);
|
||||
}
|
||||
|
||||
public void Add(Entity entity, ScriptComponent component)
|
||||
{
|
||||
if (!IsInitialized)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
ref var scriptList = ref CollectionsMarshal.GetValueRefOrAddDefault(_scriptComponents!, entity, out var exists);
|
||||
scriptList ??= new List<ScriptComponent>();
|
||||
|
||||
scriptList.Add(component);
|
||||
|
||||
component.Owner = entity;
|
||||
component._world = _world;
|
||||
component.Enable = true;
|
||||
component.Initialize();
|
||||
}
|
||||
|
||||
public bool Remove<T>(Entity entity)
|
||||
where T : ScriptComponent
|
||||
{
|
||||
if (!Has(entity)
|
||||
|| !_scriptComponents!.TryGetValue(entity, out var scriptList)
|
||||
|| scriptList == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var scriptToRemove = scriptList.FirstOrDefault(script => script is T);
|
||||
if (scriptToRemove == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
scriptToRemove.OnDestroy();
|
||||
scriptList.Remove(scriptToRemove);
|
||||
if (scriptList.Count == 0)
|
||||
{
|
||||
_scriptComponents.Remove(entity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveAt(Entity entity, int index)
|
||||
{
|
||||
if (!Has(entity)
|
||||
|| !_scriptComponents!.TryGetValue(entity, out var scriptList)
|
||||
|| scriptList == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index < 0 || index > scriptList.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
scriptList.RemoveAt(index);
|
||||
if (scriptList.Count == 0)
|
||||
{
|
||||
_scriptComponents.Remove(entity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Remove(Entity entity)
|
||||
{
|
||||
if (!Has(entity)
|
||||
|| !_scriptComponents!.TryGetValue(entity, out var scriptList)
|
||||
|| scriptList == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var script in scriptList)
|
||||
{
|
||||
script.OnDestroy();
|
||||
}
|
||||
|
||||
_scriptComponents.Remove(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Has(Entity entity)
|
||||
{
|
||||
return _scriptComponents?.ContainsKey(entity) ?? false;
|
||||
}
|
||||
|
||||
[Obsolete("Use GetAll instead of Get for ScriptComponentPool.")]
|
||||
public IComponentData Get(Entity entity)
|
||||
{
|
||||
if (!Has(entity)
|
||||
|| !_scriptComponents!.TryGetValue(entity, out var scriptList)
|
||||
|| scriptList == null
|
||||
|| scriptList.Count == 0)
|
||||
{
|
||||
return null!;
|
||||
}
|
||||
|
||||
return scriptList[0];
|
||||
}
|
||||
|
||||
[Obsolete("Use GetAll instead of GetUnsafe for ScriptComponentPool.")]
|
||||
public unsafe IntPtr GetUnsafe(Entity entity)
|
||||
{
|
||||
if (!Has(entity)
|
||||
|| !_scriptComponents!.TryGetValue(entity, out var scriptList)
|
||||
|| scriptList == null
|
||||
|| scriptList.Count == 0)
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
return (IntPtr)Unsafe.AsPointer(ref CollectionsMarshal.AsSpan(scriptList)[0]);
|
||||
}
|
||||
|
||||
public void Set(Entity entity, in IComponentData component)
|
||||
{
|
||||
if (component is not ScriptComponent scriptComponent)
|
||||
{
|
||||
throw new ArgumentException($"Component type mismatch. Expected {typeof(ScriptComponent)}, but got {component.GetType()}.");
|
||||
}
|
||||
Set(entity, scriptComponent);
|
||||
}
|
||||
|
||||
public void Set(Entity entity, in ScriptComponent component)
|
||||
{
|
||||
if (!Has(entity)
|
||||
|| !_scriptComponents!.TryGetValue(entity, out var scriptList)
|
||||
|| scriptList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var index = scriptList.IndexOf(component);
|
||||
if (index >= 0)
|
||||
{
|
||||
scriptList[index] = component;
|
||||
component.Owner = entity;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<(Entity entity, IComponentData component)> Enumerate()
|
||||
{
|
||||
if (_scriptComponents == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var kvp in _scriptComponents)
|
||||
{
|
||||
foreach (var script in kvp.Value)
|
||||
{
|
||||
yield return (kvp.Key, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IComponentData>? GetAll(Entity entity)
|
||||
{
|
||||
if (_scriptComponents == null
|
||||
|| !_scriptComponents.TryGetValue(entity, out var scriptList))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return scriptList;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_scriptComponents != null)
|
||||
{
|
||||
if (_executionList != null)
|
||||
{
|
||||
foreach (var script in _executionList)
|
||||
{
|
||||
script.OnDestroy();
|
||||
}
|
||||
|
||||
_executionList.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var scriptList in _scriptComponents.Values)
|
||||
{
|
||||
if (scriptList == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var script in scriptList)
|
||||
{
|
||||
script.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_scriptComponents.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SkipLocalsInit]
|
||||
internal struct ComponentStorage : IDisposable
|
||||
{
|
||||
private static int s_nextId = 0;
|
||||
private static class TypeID<T>
|
||||
{
|
||||
public static readonly int value = s_nextId++;
|
||||
}
|
||||
|
||||
private int _currentCapacity = 16;
|
||||
|
||||
private IComponentPool?[] _componentPools = new IComponentPool[16];
|
||||
private UnsafeBitSet[] _componentEntityMasks = new UnsafeBitSet[16];
|
||||
|
||||
private readonly Dictionary<TypeHandle, int> _typeIDMap = new(16);
|
||||
private readonly Dictionary<int, TypeHandle> _typeHandleMap = new(16);
|
||||
|
||||
private readonly ScriptComponentPool _scriptComponentPool;
|
||||
|
||||
private readonly World _world;
|
||||
|
||||
internal ComponentStorage(World world)
|
||||
{
|
||||
_world = world;
|
||||
_scriptComponentPool = new ScriptComponentPool(world);
|
||||
}
|
||||
|
||||
internal readonly IReadOnlyList<IComponentPool?> ComponentPools => _componentPools;
|
||||
internal readonly IReadOnlyList<UnsafeBitSet> ComponentEntityMasks => _componentEntityMasks;
|
||||
internal readonly ScriptComponentPool ScriptComponentPool => _scriptComponentPool;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private readonly int GetTypeID(TypeHandle typeHandle)
|
||||
{
|
||||
if (_typeIDMap.TryGetValue(typeHandle, out var id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
return typeof(TypeID<>).MakeGenericType(typeHandle!)
|
||||
.GetField("value", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
|
||||
?.GetValue(null) as int? ?? -1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private readonly int GetTypeID<T>()
|
||||
{
|
||||
return TypeID<T>.value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Resize(int newCapacity)
|
||||
{
|
||||
Array.Resize(ref _componentPools, newCapacity);
|
||||
Array.Resize(ref _componentEntityMasks, newCapacity);
|
||||
_currentCapacity = newCapacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal readonly TypeHandle GetComponentPoolType(int poolIndex)
|
||||
{
|
||||
if (poolIndex < 0 || poolIndex >= _currentCapacity)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(poolIndex), "Invalid pool index.");
|
||||
}
|
||||
|
||||
return _typeHandleMap[poolIndex];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryGetPool(TypeHandle typeHandle, [NotNullWhen(true)] out IComponentPool? pool)
|
||||
{
|
||||
var result = _typeIDMap.TryGetValue(typeHandle, out var id);
|
||||
if (!result || id >= _currentCapacity)
|
||||
{
|
||||
pool = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
pool = _componentPools[id];
|
||||
return pool != null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryGetPool(Type type, [NotNullWhen(true)] out IComponentPool? pool)
|
||||
{
|
||||
return TryGetPool(TypeHandle.Get(type), out pool);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryGetPool<T>([NotNullWhen(true)] out ComponentPool<T>? pool)
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
var id = TypeID<T>.value;
|
||||
if (id >= _currentCapacity)
|
||||
{
|
||||
pool = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
pool = (ComponentPool<T>?)_componentPools[id];
|
||||
return pool != null;
|
||||
}
|
||||
|
||||
public IComponentPool GetOrCreateComponentPool(Type type)
|
||||
{
|
||||
var typeHandle = TypeHandle.Get(type);
|
||||
if (_typeIDMap.TryGetValue(typeHandle, out var id))
|
||||
{
|
||||
if (id < _currentCapacity && _componentPools[id] is IComponentPool existingPool)
|
||||
{
|
||||
return existingPool;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
id = GetTypeID(typeHandle);
|
||||
}
|
||||
|
||||
if (id >= _currentCapacity)
|
||||
{
|
||||
Resize(_currentCapacity * 2);
|
||||
_typeIDMap[typeHandle] = id;
|
||||
_typeHandleMap[id] = typeHandle;
|
||||
}
|
||||
else if (_componentPools[id] is IComponentPool existingPool)
|
||||
{
|
||||
return existingPool;
|
||||
}
|
||||
|
||||
var pool = Activator.CreateInstance(typeof(ComponentPool<>).MakeGenericType(type)) as IComponentPool
|
||||
?? throw new InvalidOperationException($"Failed to create component pool for type {type.FullName}");
|
||||
|
||||
_componentPools[id] = pool;
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
public ComponentPool<T> GetOrCreateComponentPool<T>()
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
var id = TypeID<T>.value;
|
||||
var typeHandle = TypeHandle.Get<T>();
|
||||
|
||||
if (id >= _currentCapacity)
|
||||
{
|
||||
Resize(_currentCapacity * 2);
|
||||
_typeIDMap[typeHandle] = id;
|
||||
_typeHandleMap[id] = typeHandle;
|
||||
}
|
||||
else if (_componentPools[id] is ComponentPool<T> existingPool)
|
||||
{
|
||||
return existingPool;
|
||||
}
|
||||
|
||||
var pool = new ComponentPool<T>();
|
||||
_componentPools[id] = pool;
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryGetMask(TypeHandle typeHandle, [NotNullWhen(true)] out UnsafeBitSet? bitSet)
|
||||
{
|
||||
if (!_typeIDMap.TryGetValue(typeHandle, out var id)
|
||||
|| id >= _currentCapacity)
|
||||
{
|
||||
bitSet = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
bitSet = _componentEntityMasks[id];
|
||||
return bitSet.HasValue;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryGetMask<T>([NotNullWhen(true)] out UnsafeBitSet? bitSet)
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
return TryGetMask(TypeHandle.Get<T>(), out bitSet);
|
||||
}
|
||||
|
||||
public ref UnsafeBitSet GetOrCreateMask(TypeHandle typeHandle)
|
||||
{
|
||||
if (!_typeIDMap.TryGetValue(typeHandle, out var id))
|
||||
{
|
||||
id = GetTypeID(typeHandle);
|
||||
_typeIDMap[typeHandle] = id;
|
||||
_typeHandleMap[id] = typeHandle;
|
||||
}
|
||||
|
||||
if (id >= _currentCapacity)
|
||||
{
|
||||
Resize(_currentCapacity * 2);
|
||||
}
|
||||
|
||||
ref var set = ref _componentEntityMasks[id];
|
||||
if (!set.IsCreated)
|
||||
{
|
||||
set = new UnsafeBitSet();
|
||||
}
|
||||
|
||||
return ref set;
|
||||
}
|
||||
|
||||
public ref UnsafeBitSet GetOrCreateMask<T>()
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
return ref GetOrCreateMask(TypeHandle.Get<T>());
|
||||
}
|
||||
|
||||
public ref UnsafeBitSet GetOrCreateMask(Type type)
|
||||
{
|
||||
return ref GetOrCreateMask(TypeHandle.Get(type));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void RebuildExecutionList()
|
||||
{
|
||||
_scriptComponentPool.RebuildExecutionList();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void Remove(Entity entity)
|
||||
{
|
||||
_scriptComponentPool.Remove(entity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void Dispose()
|
||||
{
|
||||
foreach (var pool in _componentPools)
|
||||
{
|
||||
pool?.Dispose();
|
||||
}
|
||||
|
||||
foreach (var bitSet in _componentEntityMasks)
|
||||
{
|
||||
bitSet.Dispose();
|
||||
}
|
||||
|
||||
_scriptComponentPool.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace Ghost.SparseEntities.Components;
|
||||
|
||||
public interface IComponentData
|
||||
{
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
namespace Ghost.SparseEntities.Components;
|
||||
|
||||
public abstract class ScriptComponent : IComponentData
|
||||
{
|
||||
private bool _enable;
|
||||
|
||||
internal World _world = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a Value indicating whether this script component is enabled.
|
||||
/// </summary>
|
||||
public bool Enable
|
||||
{
|
||||
get => _enable;
|
||||
set
|
||||
{
|
||||
if (_enable == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_enable = value;
|
||||
if (_enable)
|
||||
{
|
||||
OnEnable();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDisable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity that owns this script component.
|
||||
/// </summary>
|
||||
public Entity Owner
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the EntityManager instance associated with the current world.
|
||||
/// </summary>
|
||||
protected EntityManager EntityManager => _world.EntityManager;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the priority of the script component.
|
||||
/// Change this during runtime does not affect the execution order.
|
||||
/// </summary>
|
||||
public virtual int ExecutionOrder => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the script component is enabled.
|
||||
/// </summary>
|
||||
public virtual void OnEnable()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the script component is disabled.
|
||||
/// </summary>
|
||||
public virtual void OnDisable()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the script component is initialized.
|
||||
/// </summary>
|
||||
public virtual void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the script component is started.
|
||||
/// </summary>
|
||||
public virtual void Start()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every frame.
|
||||
/// </summary>
|
||||
public virtual void Update()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every frame after all Update methods have been called.
|
||||
/// </summary>
|
||||
public virtual void LateUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called at a fixed interval.
|
||||
/// This method is called at a fixed time step, independent of the frame rate.
|
||||
/// </summary>
|
||||
public virtual void FixedUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the script component is destroyed.
|
||||
/// </summary>
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ghost.SparseEntities;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public struct Entity : IEquatable<Entity>, IComparable<Entity>
|
||||
{
|
||||
public const EntityID INVALID_ID = -1;
|
||||
|
||||
[JsonInclude]
|
||||
private EntityID _id;
|
||||
private GenerationID _generation;
|
||||
|
||||
public readonly EntityID ID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _id;
|
||||
}
|
||||
|
||||
public readonly GenerationID Generation
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _generation;
|
||||
}
|
||||
|
||||
public readonly bool IsValid
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ID != INVALID_ID;
|
||||
}
|
||||
|
||||
public static Entity Invalid
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(INVALID_ID, GenerationID.MaxValue);
|
||||
}
|
||||
|
||||
internal Entity(EntityID id, GenerationID generation)
|
||||
{
|
||||
_id = id;
|
||||
_generation = generation;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void IncrementGeneration() => _generation++;
|
||||
|
||||
public readonly bool Equals(Entity other)
|
||||
{
|
||||
return _id == other._id && _generation == other._generation;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Entity other)
|
||||
{
|
||||
return _id.CompareTo(other._id);
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Entity other && Equals(other);
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return _id.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator ==(Entity left, Entity right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Entity left, Entity right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return $"Entity {{ Index: {ID}, Generation: {Generation} }}";
|
||||
}
|
||||
}
|
||||
@@ -1,360 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.SparseEntities.Components;
|
||||
using Ghost.SparseEntities.Query;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.SparseEntities;
|
||||
|
||||
public readonly struct EntityManager : IDisposable
|
||||
{
|
||||
private readonly List<Entity> _entities;
|
||||
private readonly Queue<EntityID> _freeEntitySlots;
|
||||
|
||||
private readonly World _world;
|
||||
|
||||
public readonly int EntityCount => _entities.Count;
|
||||
public readonly ReadOnlySpan<Entity> Entities => CollectionsMarshal.AsSpan(_entities);
|
||||
|
||||
internal EntityManager(World world, int initialCapacity)
|
||||
{
|
||||
_entities = new(initialCapacity);
|
||||
_freeEntitySlots = new(initialCapacity);
|
||||
_world = world;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new <see cref="Entity"/> to the world.
|
||||
/// </summary>
|
||||
/// <returns>The created <see cref="Entity"/>.</returns>
|
||||
public readonly Entity CreateEntity()
|
||||
{
|
||||
Entity entity;
|
||||
if (_freeEntitySlots.TryDequeue(out var id))
|
||||
{
|
||||
entity = _entities[id];
|
||||
}
|
||||
else
|
||||
{
|
||||
id = _entities.Count;
|
||||
entity = new Entity(id, 0);
|
||||
_entities.Add(entity);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal readonly void AddEntityInternal(Entity entity)
|
||||
{
|
||||
_entities.Add(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified <see cref="Entity"/> from the world.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public readonly void RemoveEntity(ref Entity entity)
|
||||
{
|
||||
if (entity.ID >= _entities.Count || _entities[entity.ID].Generation != entity.Generation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_world.ComponentStorage.Remove(entity);
|
||||
|
||||
var slot = _entities[entity.ID];
|
||||
slot.IncrementGeneration();
|
||||
_entities[entity.ID] = slot;
|
||||
_freeEntitySlots.Enqueue(entity.ID);
|
||||
|
||||
entity = Entity.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given <see cref="Entity"/> is valid and belongs to this <see cref="World"/>.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to check.</param>
|
||||
/// <returns>True if the entity is valid and belongs to this world; otherwise, false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool HasEntity(Entity entity)
|
||||
{
|
||||
if (!entity.IsValid
|
||||
|| entity.ID >= _entities.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _entities[entity.ID].Generation == entity.Generation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a component of the specified type to the given entity.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method use reflection to determine the type of the component being added. Use generic as much as possible.
|
||||
/// </remarks>
|
||||
/// <param name="entity">The entity to which the component will be added.</param>
|
||||
/// <param name="component">The component data to associate with the entity.</param>
|
||||
/// <param name="componentType">The type of the component being added. This must match the type of <paramref name="component"/>.</param>
|
||||
public readonly void AddComponent(Entity entity, IComponentData component, Type componentType)
|
||||
{
|
||||
_world.ComponentStorage.GetOrCreateComponentPool(componentType).Add(entity, component);
|
||||
_world.ComponentStorage.GetOrCreateMask(componentType).SetBit(entity.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a component of type <typeparamref name="T"/> to the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the component to set.</typeparam>
|
||||
/// <param name="entity">The entity for which the component is to be add.</param>
|
||||
/// <param name="component">The component Value to add.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void AddComponent<T>(Entity entity, T component)
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
_world.ComponentStorage.GetOrCreateComponentPool<T>().Add(entity, component);
|
||||
_world.ComponentStorage.GetOrCreateMask<T>().SetBit(entity.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a component of type <typeparamref name="T"/> from the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the component to remove.</typeparam>
|
||||
/// <param name="entity">The entity for which the component is to be remove.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool RemoveComponent<T>(Entity entity)
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
if (!_world.ComponentStorage.TryGetPool<T>(out var pool) || !pool.Has(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pool.Remove(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_world.ComponentStorage.GetOrCreateMask<T>().ClearBit(entity.ID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a component of the specified type for the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity for which the component is to be set.</param>
|
||||
/// <param name="component">The component Value to set.</param>
|
||||
/// <param name="type">The type of the component to set.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void SetComponent(Entity entity, IComponentData component, Type type)
|
||||
{
|
||||
var typeHandle = TypeHandle.Get(type);
|
||||
if (!_world.ComponentStorage.TryGetPool(typeHandle, out var pool))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pool.Has(entity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pool.Set(entity, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a component of type <typeparamref name="T"/> for the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the component to set.</typeparam>
|
||||
/// <param name="entity">The entity for which the component is to be set.</param>
|
||||
/// <param name="component">The component Value to set.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void SetComponent<T>(Entity entity, in T component)
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
_world.ComponentStorage.GetOrCreateComponentPool<T>().Set(entity, in component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given <see cref="Entity"/> has a component of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to check.</param>
|
||||
/// <param name="typeHandle">The handle of the component type.</param>
|
||||
/// <returns>True if the entity has the component; otherwise, false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool HasComponent(Entity entity, TypeHandle typeHandle)
|
||||
{
|
||||
return _world.ComponentStorage.TryGetMask(typeHandle, out var bitSet) && bitSet.Value.IsSet(entity.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a reference to a component of type <typeparamref name="T"/> associated with the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the component to retrieve.</typeparam>
|
||||
/// <param name="entity">The entity whose component is to be retrieved.</param>
|
||||
/// <returns>A <see cref="CompRef{T}"/> to the component, or a null reference if the component does not exist.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly CompRef<T> GetComponent<T>(Entity entity)
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
if (_world.ComponentStorage.TryGetPool<T>(out var pool) && pool.Has(entity))
|
||||
{
|
||||
return new CompRef<T>(ref pool.GetRef(entity));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CompRef<T>(ref Unsafe.NullRef<T>(), false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all components associated with the specified entity.
|
||||
/// </summary>
|
||||
/// <remarks>This method iterates through all available component pools to find components associated
|
||||
/// with the given entity. It is designed to lazily yield components, making it efficient for scenarios where only
|
||||
/// a subset of components may be needed.</remarks>
|
||||
/// <param name="entity">The entity for which components are to be retrieved.</param>
|
||||
/// <returns>An enumerable collection of components associated with the specified entity. If the entity has no components,
|
||||
/// the collection will be empty.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly IEnumerable<IComponentData> GetComponents(Entity entity)
|
||||
{
|
||||
foreach (var pool in _world.ComponentStorage.ComponentPools)
|
||||
{
|
||||
if (pool == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pool.Has(entity))
|
||||
{
|
||||
yield return pool.Get(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an enumerable collection of raw pointers to the components associated with the specified entity.
|
||||
/// </summary>
|
||||
/// <remarks>This method provides direct access to the memory locations of components, bypassing type
|
||||
/// safety. Use with caution, as improper handling of raw pointers can lead to undefined behavior or memory
|
||||
/// corruption. Ensure that the entity is valid and exists within the current world context before calling this
|
||||
/// method.</remarks>
|
||||
/// <param name="entity">The entity whose components are to be retrieved.</param>
|
||||
/// <returns>An enumerable collection of <see cref="IntPtr"/> representing the memory addresses of the components associated
|
||||
/// with the specified entity. The collection will be empty if the entity has no associated components.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly IEnumerable<(TypeHandle, IntPtr)> GetComponentsUnsafe(Entity entity)
|
||||
{
|
||||
for (var i = 0; i < _world.ComponentStorage.ComponentPools.Count; i++)
|
||||
{
|
||||
var pool = _world.ComponentStorage.ComponentPools[i];
|
||||
if (pool == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pool.Has(entity))
|
||||
{
|
||||
yield return (_world.ComponentStorage.GetComponentPoolType(i), pool.GetUnsafe(entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a script of type <typeparamref name="T"/> to the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the script to add.</typeparam>
|
||||
/// <param name="entity">The entity to which the script is to be added.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void AddScript<T>(Entity entity)
|
||||
where T : ScriptComponent, new()
|
||||
{
|
||||
_world.ComponentStorage.ScriptComponentPool.Add(entity, new T());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a script of the specified type to the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to which the script is to be added.</param>
|
||||
/// <param name="type">The type of the script to add.</param>
|
||||
/// <exception cref="ArgumentException">Thrown if the specified type does not inherit from <see cref="ScriptComponent"/>.</exception>
|
||||
/// <exception cref="InvalidOperationException">Thrown if the script instance could not be created.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void AddScript(Entity entity, Type type)
|
||||
{
|
||||
if (!typeof(ScriptComponent).IsAssignableFrom(type))
|
||||
{
|
||||
throw new ArgumentException($"Type {type} must inherit from ScriptComponent.", nameof(type));
|
||||
}
|
||||
|
||||
var instance = (ScriptComponent?)Activator.CreateInstance(type) ?? throw new InvalidOperationException($"Failed to create instance of {type}.");
|
||||
_world.ComponentStorage.ScriptComponentPool.Add(entity, instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a script of type <typeparamref name="T"/> from the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the script to remove.</typeparam>
|
||||
/// <param name="entity">The entity from which the script is to be removed.</param>
|
||||
/// <returns>True if the script was successfully removed; otherwise, false.</returns>
|
||||
public readonly bool RemoveScript<T>(Entity entity)
|
||||
where T : ScriptComponent
|
||||
{
|
||||
if (!_world.ComponentStorage.ScriptComponentPool.Remove<T>(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a script at the specified index from the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity from which the script is to be removed.</param>
|
||||
/// <param name="index">The index of the script to remove.</param>
|
||||
/// <returns>True if the script was successfully removed; otherwise, false.</returns>
|
||||
public readonly bool RemoveScriptAt(Entity entity, int index)
|
||||
{
|
||||
if (!_world.ComponentStorage.ScriptComponentPool.RemoveAt(entity, index))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the first script of type <typeparamref name="T"/> associated with the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the script to retrieve.</typeparam>
|
||||
/// <param name="entity">The entity whose script is to be retrieved.</param>
|
||||
/// <returns>The script of type <typeparamref name="T"/>, or null if no such script exists.</returns>
|
||||
public readonly T? GetScript<T>(Entity entity)
|
||||
where T : ScriptComponent
|
||||
{
|
||||
return (T?)_world.ComponentStorage.ScriptComponentPool.GetAll(entity)?
|
||||
.FirstOrDefault(script => script is T tScript);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all scripts of type <typeparamref name="T"/> associated with the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the scripts to retrieve.</typeparam>
|
||||
/// <param name="entity">The entity whose scripts are to be retrieved.</param>
|
||||
/// <returns>An enumerable of scripts of type <typeparamref name="T"/>.</returns>
|
||||
public readonly IEnumerable<T> GetScripts<T>(Entity entity)
|
||||
where T : ScriptComponent
|
||||
{
|
||||
return (IEnumerable<T>?)_world.ComponentStorage.ScriptComponentPool.GetAll(entity)?.Where(script => script is T tScript) ?? Enumerable.Empty<T>();
|
||||
}
|
||||
|
||||
public readonly void Dispose()
|
||||
{
|
||||
_entities.Clear();
|
||||
_freeEntitySlots.Clear();
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Template\ForEach.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ForEach.tt</DependentUpon>
|
||||
</None>
|
||||
<None Include="Template\QueryItem.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>QueryItem.tt</DependentUpon>
|
||||
</None>
|
||||
<None Include="Template\QueryRefComponent.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>QueryRefComponent.tt</DependentUpon>
|
||||
</None>
|
||||
<None Include="Template\World.Query.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>World.Query.tt</DependentUpon>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Template\ForEach.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>ForEach.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Template\QueryEnumerable.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>QueryEnumerable.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Template\Helpers.tt">
|
||||
<Generator>TextTemplatingFilePreprocessor</Generator>
|
||||
<LastGenOutput>Helpers.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Template\QueryItem.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>QueryItem.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Template\QueryRefComponent.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>QueryRefComponent.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Template\World.Query.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>World.Query.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Template\ForEach.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ForEach.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Template\Helpers.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Helpers.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Template\QueryEnumerable.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>QueryEnumerable.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Template\QueryItem.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>QueryItem.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Template\QueryRefComponent.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>QueryRefComponent.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Template\World.Query.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>World.Query.tt</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Helpers\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,42 +0,0 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.SparseEntities.Query;
|
||||
|
||||
public struct QueryBuilder
|
||||
{
|
||||
private QueryFilter _filter;
|
||||
|
||||
public QueryBuilder()
|
||||
{
|
||||
_filter = new QueryFilter();
|
||||
}
|
||||
|
||||
public QueryBuilder WithAll<T>()
|
||||
{
|
||||
_filter._all.Add(TypeHandle.Get<T>());
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryBuilder WithAny<T>()
|
||||
{
|
||||
_filter._any.Add(TypeHandle.Get<T>());
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryBuilder WithAbsent<T>()
|
||||
{
|
||||
_filter._absent.Add(TypeHandle.Get<T>());
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryBuilder WithDisabled<T>()
|
||||
{
|
||||
_filter._disabled.Add(TypeHandle.Get<T>());
|
||||
return this;
|
||||
}
|
||||
|
||||
public readonly QueryFilter Build()
|
||||
{
|
||||
return _filter;
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.SparseEntities.Query;
|
||||
|
||||
public struct QueryFilter : IDisposable
|
||||
{
|
||||
//private readonly Stack.Scope _scope;
|
||||
|
||||
internal UnsafeList<TypeHandle> _all;
|
||||
internal UnsafeList<TypeHandle> _any;
|
||||
internal UnsafeList<TypeHandle> _absent;
|
||||
internal UnsafeList<TypeHandle> _disabled;
|
||||
|
||||
public QueryFilter()
|
||||
{
|
||||
//_scope = AllocationManager.CreateStackScope();
|
||||
|
||||
_all = new UnsafeList<TypeHandle>(4, Allocator.Stack);
|
||||
_any = new UnsafeList<TypeHandle>(4, Allocator.Stack);
|
||||
_absent = new UnsafeList<TypeHandle>(4, Allocator.Stack);
|
||||
_disabled = new UnsafeList<TypeHandle>(4, Allocator.Stack);
|
||||
}
|
||||
|
||||
public readonly UnsafeBitSet ComputeFilterBitMask(World world, Allocator allocator)
|
||||
{
|
||||
UnsafeBitSet allMask = default;
|
||||
UnsafeBitSet anyMask = default;
|
||||
UnsafeBitSet absentMask = default;
|
||||
|
||||
var result = new UnsafeBitSet(world.EntityManager.EntityCount, allocator);
|
||||
result.ClearAll();
|
||||
|
||||
using (AllocationManager.CreateStackScope())
|
||||
{
|
||||
foreach (var typeHandle in _all)
|
||||
{
|
||||
var mask = world.ComponentStorage.GetOrCreateMask(typeHandle);
|
||||
|
||||
if (!allMask.IsCreated)
|
||||
{
|
||||
allMask = new UnsafeBitSet(mask.Count, Allocator.Stack, AllocationOption.None);
|
||||
allMask.SetAll();
|
||||
}
|
||||
|
||||
allMask.And(mask);
|
||||
}
|
||||
|
||||
foreach (var typeHandle in _any)
|
||||
{
|
||||
var mask = world.ComponentStorage.GetOrCreateMask(typeHandle);
|
||||
|
||||
if (!anyMask.IsCreated)
|
||||
{
|
||||
anyMask = new UnsafeBitSet(mask.Count, Allocator.Stack);
|
||||
}
|
||||
|
||||
anyMask.And(mask);
|
||||
}
|
||||
|
||||
foreach (var typeHandle in _absent)
|
||||
{
|
||||
var mask = world.ComponentStorage.GetOrCreateMask(typeHandle);
|
||||
|
||||
if (!absentMask.IsCreated)
|
||||
{
|
||||
absentMask = new UnsafeBitSet(mask.Count, Allocator.Stack);
|
||||
}
|
||||
|
||||
absentMask.Or(mask);
|
||||
}
|
||||
|
||||
if (allMask.IsCreated)
|
||||
{
|
||||
result.And(allMask);
|
||||
}
|
||||
|
||||
if (anyMask.IsCreated)
|
||||
{
|
||||
result.And(anyMask);
|
||||
}
|
||||
|
||||
if (absentMask.IsCreated)
|
||||
{
|
||||
result.ANDC(absentMask);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public readonly void Dispose()
|
||||
{
|
||||
//_scope.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
using Ghost.SparseEntities.Components;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.SparseEntities.Query;
|
||||
|
||||
public interface IQueryTypeParameter<T>
|
||||
where T : IComponentData
|
||||
{
|
||||
}
|
||||
|
||||
public ref struct CompRef<T> : IQueryTypeParameter<T>
|
||||
where T : IComponentData
|
||||
{
|
||||
internal ref T _value;
|
||||
|
||||
public ref T ValueRW
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ref _value;
|
||||
}
|
||||
|
||||
public readonly ref T ValueRO
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ref _value;
|
||||
}
|
||||
|
||||
public readonly bool IsValid
|
||||
{
|
||||
get;
|
||||
init;
|
||||
}
|
||||
|
||||
public CompRef(ref T value, bool isValid)
|
||||
{
|
||||
_value = ref value;
|
||||
IsValid = isValid;
|
||||
}
|
||||
|
||||
public CompRef(ref T value)
|
||||
: this(ref value, true)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public readonly ref struct CompRO<T> : IQueryTypeParameter<T>
|
||||
where T : IComponentData
|
||||
{
|
||||
internal readonly ref T _value;
|
||||
|
||||
public readonly ref T ValueRO
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ref _value;
|
||||
}
|
||||
|
||||
public readonly bool IsValid
|
||||
{
|
||||
get;
|
||||
init;
|
||||
}
|
||||
|
||||
public CompRO(ref T value, bool isValid)
|
||||
{
|
||||
_value = ref value;
|
||||
IsValid = isValid;
|
||||
}
|
||||
|
||||
public CompRO(ref T value)
|
||||
: this(ref value, true)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
namespace Ghost.SparseEntities.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Attribute to declare that a system depends on one or more other systems.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false)]
|
||||
public class DependsOnAttribute : Attribute
|
||||
{
|
||||
public Type[] Prerequisites
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public DependsOnAttribute(params Type[] prerequisites)
|
||||
{
|
||||
Prerequisites = prerequisites;
|
||||
}
|
||||
}
|
||||
|
||||
public interface ISystem
|
||||
{
|
||||
public void OnCreate(in SystemState state);
|
||||
|
||||
public void OnUpdate(in SystemState state);
|
||||
|
||||
public void OnDestroy(in SystemState state);
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ghost.SparseEntities.Systems;
|
||||
|
||||
internal class SystemDependencyBuilder
|
||||
{
|
||||
private readonly Dictionary<Type, List<Type>> _dependencies = new();
|
||||
private readonly List<Type> _systemTypes;
|
||||
|
||||
public SystemDependencyBuilder(List<Type> allSystemTypes)
|
||||
{
|
||||
_systemTypes = allSystemTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a dependency graph for all system types that implement the <see cref="ISystem"/> interface.
|
||||
/// </summary>
|
||||
/// <remarks>This method analyzes all system types and their dependencies, as defined by the <see
|
||||
/// cref="DependsOnAttribute"/>. It validates that each system type is a concrete implementation of <see
|
||||
/// cref="ISystem"/> and constructs a mapping of each system type to its direct dependencies.</remarks>
|
||||
/// <exception cref="ArgumentException">Thrown if a type in <c>allSystemTypes</c> is not a concrete implementation of <see cref="ISystem"/>.</exception>
|
||||
public void BuildDependencyGraph()
|
||||
{
|
||||
foreach (var systemType in _systemTypes)
|
||||
{
|
||||
if (!typeof(ISystem).IsAssignableFrom(systemType) || systemType.IsAbstract || systemType.IsInterface)
|
||||
{
|
||||
throw new ArgumentException($"{systemType.Name} is not a concrete ISystem type.");
|
||||
}
|
||||
|
||||
var directDependencies = new List<Type>();
|
||||
var dependsOnAttributes = systemType.GetCustomAttributes<DependsOnAttribute>(false);
|
||||
foreach (var attr in dependsOnAttributes)
|
||||
{
|
||||
directDependencies.AddRange(attr.Prerequisites);
|
||||
}
|
||||
|
||||
_dependencies[systemType] = directDependencies;
|
||||
}
|
||||
}
|
||||
|
||||
private void Visit(Type systemType, HashSet<Type> visited, HashSet<Type> permanentMark, List<Type> executionOrder)
|
||||
{
|
||||
if (permanentMark.Contains(systemType))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (visited.Contains(systemType))
|
||||
{
|
||||
throw new InvalidOperationException($"Circular dependency detected involving system: {systemType.Name}");
|
||||
}
|
||||
|
||||
visited.Add(systemType); // Mark as currently visiting
|
||||
|
||||
if (_dependencies.TryGetValue(systemType, out var directDependencies))
|
||||
{
|
||||
foreach (var dependencyType in directDependencies)
|
||||
{
|
||||
// Ensure the dependency is a registered system type
|
||||
if (!_systemTypes.Contains(dependencyType))
|
||||
{
|
||||
throw new InvalidOperationException($"System {systemType.Name} depends on unregistered system {dependencyType.Name}.");
|
||||
}
|
||||
|
||||
Visit(dependencyType, visited, permanentMark, executionOrder);
|
||||
}
|
||||
}
|
||||
|
||||
visited.Remove(systemType); // Done visiting this node in the current path
|
||||
permanentMark.Add(systemType); // Mark as permanently processed
|
||||
executionOrder.Add(systemType); // Add to the sorted list (this will be reversed later for correct order)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the topological order of systems.
|
||||
/// </summary>
|
||||
/// <returns>A list of system types in the order they should be executed.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown if a circular dependency is detected.</exception>"
|
||||
public List<Type> BuildExecutionOrder()
|
||||
{
|
||||
var executionOrder = new List<Type>(_systemTypes.Count);
|
||||
var visited = new HashSet<Type>(); // Tracks visited nodes in the current DFS path (for cycle detection)
|
||||
var permanentMark = new HashSet<Type>(); // Tracks nodes whose dependencies have been fully resolved
|
||||
|
||||
// Initialize dependencies for all registered systems, even those without explicit attributes
|
||||
foreach (var sysType in _systemTypes)
|
||||
{
|
||||
if (!_dependencies.ContainsKey(sysType))
|
||||
{
|
||||
_dependencies[sysType] = new();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var systemType in _systemTypes)
|
||||
{
|
||||
if (!permanentMark.Contains(systemType))
|
||||
{
|
||||
Visit(systemType, visited, permanentMark, executionOrder);
|
||||
}
|
||||
}
|
||||
|
||||
return executionOrder;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Ghost.SparseEntities.Systems;
|
||||
|
||||
public struct SystemState
|
||||
{
|
||||
public World World
|
||||
{
|
||||
get;
|
||||
init;
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.SparseEntities.Systems;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public readonly struct SystemStorage
|
||||
{
|
||||
private readonly List<Type> _systems = new();
|
||||
private readonly List<ISystem> _executionList = new();
|
||||
|
||||
private readonly World _world;
|
||||
|
||||
internal ReadOnlySpan<Type> Systems => CollectionsMarshal.AsSpan(_systems);
|
||||
|
||||
internal SystemStorage(World world)
|
||||
{
|
||||
_world = world;
|
||||
}
|
||||
|
||||
public readonly void AddSystem(Type systemType)
|
||||
{
|
||||
_systems.Add(systemType);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void AddSystem<T>()
|
||||
where T : ISystem, new()
|
||||
{
|
||||
AddSystem(typeof(T));
|
||||
}
|
||||
|
||||
public readonly void RemoveSystem(Type systemType)
|
||||
{
|
||||
_systems.Remove(systemType);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly void RemoveSystem<T>()
|
||||
where T : ISystem, new()
|
||||
{
|
||||
RemoveSystem(typeof(T));
|
||||
}
|
||||
|
||||
internal void CreateSystems()
|
||||
{
|
||||
var builder = new SystemDependencyBuilder(_systems);
|
||||
builder.BuildDependencyGraph();
|
||||
var executionOrder = builder.BuildExecutionOrder();
|
||||
|
||||
var state = new SystemState()
|
||||
{
|
||||
World = _world,
|
||||
};
|
||||
|
||||
foreach (var systemType in executionOrder)
|
||||
{
|
||||
var system = (ISystem?)Activator.CreateInstance(systemType) ?? throw new InvalidOperationException($"Failed to create instance of system type {systemType.Name}.");
|
||||
|
||||
_executionList.Add(system);
|
||||
system.OnCreate(in state);
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateSystems()
|
||||
{
|
||||
var state = new SystemState()
|
||||
{
|
||||
World = _world,
|
||||
};
|
||||
|
||||
foreach (var system in _executionList)
|
||||
{
|
||||
system.OnUpdate(in state);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Dispose()
|
||||
{
|
||||
var state = new SystemState()
|
||||
{
|
||||
World = _world,
|
||||
};
|
||||
|
||||
foreach (var system in _executionList)
|
||||
{
|
||||
system.OnDestroy(in state);
|
||||
}
|
||||
|
||||
_systems.Clear();
|
||||
_executionList.Clear();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
|
||||
namespace Ghost.SparseEntities;
|
||||
|
||||
public delegate void ForEach<T0>(ref T0 t0Component);
|
||||
public delegate void ForEach<T0, T1>(ref T0 t0Component,ref T1 t1Component);
|
||||
public delegate void ForEach<T0, T1, T2>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component);
|
||||
public delegate void ForEach<T0, T1, T2, T3>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component);
|
||||
public delegate void ForEach<T0, T1, T2, T3, T4>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component);
|
||||
public delegate void ForEach<T0, T1, T2, T3, T4, T5>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component);
|
||||
public delegate void ForEach<T0, T1, T2, T3, T4, T5, T6>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component);
|
||||
public delegate void ForEach<T0, T1, T2, T3, T4, T5, T6, T7>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component,ref T7 t7Component);
|
||||
@@ -1,16 +0,0 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ include file="Helpers.ttinclude" #>
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
<# for (var i = 1; i <= Amount; i++)
|
||||
{
|
||||
var generics = AppendGenerics(i);
|
||||
var compGenerics = AppendGenericRefParameters(i);
|
||||
#>
|
||||
public delegate void ForEach<<#= generics #>>(<#= compGenerics #>);
|
||||
<# } #>
|
||||
@@ -1,128 +0,0 @@
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#+
|
||||
|
||||
public int Amount = 8;
|
||||
public int ExtensionAmount = 3;
|
||||
|
||||
public string Indent(StringBuilder sb, int spaces)
|
||||
{
|
||||
var indent = new string(' ', spaces);
|
||||
return sb.ToString().Replace("\n", "\n" + indent);
|
||||
}
|
||||
|
||||
string AppendGenerics(int amount, string template)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < amount; i++)
|
||||
{
|
||||
if (i > 0) sb.Append(", ");
|
||||
sb.Append(string.Format(template, i));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
string AppendGenerics(int amount)
|
||||
{
|
||||
return AppendGenerics(amount, "T{0}");
|
||||
}
|
||||
|
||||
public StringBuilder AppendGenericRefParameters(int amount)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var localIndex = 0; localIndex < amount; localIndex++)
|
||||
{
|
||||
sb.Append($"ref T{localIndex} t{localIndex}Component,");
|
||||
}
|
||||
|
||||
sb.Length--;
|
||||
return sb;
|
||||
}
|
||||
|
||||
public StringBuilder AppendRefParameters(int amount)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var localIndex = 0; localIndex < amount; localIndex++)
|
||||
{
|
||||
sb.Append($"ref component{localIndex},");
|
||||
}
|
||||
|
||||
sb.Length--;
|
||||
return sb;
|
||||
}
|
||||
|
||||
public StringBuilder AppendGenericRestrictions(int amount, string Ttemplate, string template)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var localIndex = 0; localIndex < amount; localIndex++)
|
||||
{
|
||||
sb.Append($"where {Ttemplate}{localIndex} : {template}");
|
||||
if (localIndex < amount - 1)
|
||||
{
|
||||
sb.Append(' ');
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
public StringBuilder AppendGenericRestrictions(int amount, string template)
|
||||
{
|
||||
return AppendGenericRestrictions(amount, "T", template);
|
||||
}
|
||||
|
||||
public StringBuilder TryGetComponentPools(int amount)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var localIndex = 0; localIndex < amount; localIndex++)
|
||||
{
|
||||
sb.Append($"_componentStorage.TryGetPool<T{localIndex}>(out var pool{localIndex})");
|
||||
if (localIndex < amount - 1)
|
||||
{
|
||||
sb.Append(" && ");
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
public StringBuilder HasEntity(int amount)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var localIndex = 1; localIndex < amount; localIndex++)
|
||||
{
|
||||
sb.Append($"pool{localIndex}.Has(entity)");
|
||||
if (localIndex < amount - 1)
|
||||
{
|
||||
sb.Append(" && ");
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
public StringBuilder GetComponent(int amount)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var localIndex = 0; localIndex < amount; localIndex++)
|
||||
{
|
||||
sb.Append($"pool{localIndex}.GetRef(entity)");
|
||||
if (localIndex < amount - 1)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
public StringBuilder GetComponentRef(int amount)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var localIndex = 0; localIndex < amount; localIndex++)
|
||||
{
|
||||
sb.Append($"ref pool{localIndex}.GetRef(entity)");
|
||||
if (localIndex < amount - 1)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
#>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,175 +0,0 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ include file="Helpers.ttinclude" #>
|
||||
|
||||
using Ghost.Core;
|
||||
using Ghost.Entities.Components;
|
||||
using Ghost.Entities.Query;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
<# for (int arity = 1; arity <= Amount; arity++) {
|
||||
var generics = AppendGenerics(arity);
|
||||
var restrictions = AppendGenericRestrictions(arity, "unmanaged, IComponentData");
|
||||
var poolParams = Enumerable.Range(0, arity)
|
||||
.Select(i => $"ComponentPool<T{i}> pool{i}")
|
||||
.Aggregate((a, b) => a + ", " + b);
|
||||
var constructorParams = Enumerable.Range(0, arity)
|
||||
.Select(i => $"_pool{i}")
|
||||
.Aggregate((a, b) => a + ", " + b);
|
||||
|
||||
#>
|
||||
public unsafe ref struct QueryEnumerable<<#= generics #>>
|
||||
<#= restrictions #>
|
||||
{
|
||||
private QueryFilter _filter;
|
||||
|
||||
private readonly World _world;
|
||||
|
||||
<# for (int i = 0; i < arity; i++){ #>
|
||||
private readonly ComponentPool<T<#= i #>> _pool<#= i #>;
|
||||
<# } #>
|
||||
private readonly int _count;
|
||||
|
||||
internal QueryEnumerable(World world, <#= poolParams #>, int count)
|
||||
{
|
||||
_filter = new();
|
||||
<# for (int i = 0; i < arity; i++) {#>
|
||||
_filter._all.Add(TypeHandle.Get<T<#= i #>>());
|
||||
<# } #>
|
||||
|
||||
_world = world;
|
||||
|
||||
<# for (int i = 0; i < arity; i++) { #>
|
||||
_pool<#= i #> = pool<#= i #>;
|
||||
<# } #>
|
||||
|
||||
_count = count;
|
||||
}
|
||||
|
||||
internal QueryEnumerable(World world, <#= poolParams #>, int count, ref readonly QueryFilter filter)
|
||||
{
|
||||
_filter = filter;
|
||||
|
||||
_world = world;
|
||||
|
||||
<# for (int i = 0; i < arity; i++) { #>
|
||||
_pool<#= i #> = pool<#= i #>;
|
||||
<# } #>
|
||||
|
||||
_count = count;
|
||||
}
|
||||
|
||||
#pragma warning disable CS9084 // Struct member returns 'this' or other instance members by reference
|
||||
public Enumerator GetEnumerator() => new(_world, <#= constructorParams #>, _count, ref _filter);
|
||||
#pragma warning restore CS9084 // Struct member returns 'this' or other instance members by reference
|
||||
|
||||
public ref struct Enumerator
|
||||
{
|
||||
private ref QueryFilter _filter;
|
||||
private UnsafeBitSet _filterMask;
|
||||
|
||||
private readonly ReadOnlySpan<Entity> _entities;
|
||||
private readonly Stack.Scope _stackScope;
|
||||
|
||||
<# for (int i = 0; i < arity; i++){ #>
|
||||
private readonly ComponentPool<T<#= i #>> _pool<#= i #>;
|
||||
<# } #>
|
||||
|
||||
private int _index;
|
||||
private int _count;
|
||||
|
||||
public QueryItem<<#= generics #>> Current
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal Enumerator(World world, <#= poolParams #>, int count, ref QueryFilter filter)
|
||||
{
|
||||
_stackScope = AllocationManager.CreateStackScope();
|
||||
|
||||
_filter = ref filter;
|
||||
_filterMask = _filter.ComputeFilterBitMask(world, Allocator.Stack);
|
||||
|
||||
_entities = world.EntityManager.Entities;
|
||||
|
||||
<# for (int i = 0; i < arity; i++){ #>
|
||||
_pool<#= i #> = pool<#= i #>;
|
||||
<# } #>
|
||||
|
||||
_count = count;
|
||||
_index = -1;
|
||||
|
||||
Current = default;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index = _filterMask.NextSetBit(_index + 1);
|
||||
if (_index < 0 || _count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_count--;
|
||||
Current = new QueryItem<<#= generics #>>(_entities[_index], <#= constructorParams #>);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public readonly void Dispose()
|
||||
{
|
||||
_stackScope.Dispose();
|
||||
_filter.Dispose();
|
||||
}
|
||||
}
|
||||
<# for (int i = 1; i <= ExtensionAmount; i++) {
|
||||
var compGenerics = AppendGenerics(i, "TComponent");
|
||||
var compRestrictions = AppendGenericRestrictions(i, "TComponent", "unmanaged, IComponentData");
|
||||
#>
|
||||
|
||||
public QueryEnumerable<<#= generics #>> WithAll<<#= compGenerics #>>()
|
||||
<#= compRestrictions #>
|
||||
{
|
||||
<# for (int j = 0; j < i; j++) {#>
|
||||
_filter._all.Add(TypeHandle.Get<TComponent<#= j #>>());
|
||||
<# } #>
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryEnumerable<<#= generics #>> WithAny<<#= compGenerics #>>()
|
||||
<#= compRestrictions #>
|
||||
{
|
||||
<# for (int j = 0; j < i; j++) {#>
|
||||
_filter._any.Add(TypeHandle.Get<TComponent<#= j #>>());
|
||||
<# } #>
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryEnumerable<<#= generics #>> WithAbsent<<#= compGenerics #>>()
|
||||
<#= compRestrictions #>
|
||||
{
|
||||
<# for (int j = 0; j < i; j++) {#>
|
||||
_filter._absent.Add(TypeHandle.Get<TComponent<#= j #>>());
|
||||
<# } #>
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryEnumerable<<#= generics #>> WithDisabled<<#= compGenerics #>>()
|
||||
<#= compRestrictions #>
|
||||
{
|
||||
<# for (int j = 0; j < i; j++) {#>
|
||||
_filter._disabled.Add(TypeHandle.Get<TComponent<#= j #>>());
|
||||
<# } #>
|
||||
return this;
|
||||
}
|
||||
<# } #>
|
||||
}
|
||||
|
||||
<# } #>
|
||||
@@ -1,319 +0,0 @@
|
||||
|
||||
|
||||
using Ghost.SparseEntities.Components;
|
||||
using Ghost.SparseEntities.Query;
|
||||
|
||||
namespace Ghost.SparseEntities;
|
||||
|
||||
public readonly struct QueryItem<T0>
|
||||
where T0 : unmanaged, IComponentData
|
||||
{
|
||||
private readonly Entity _entity;
|
||||
private readonly ComponentPool<T0> _pool0;
|
||||
|
||||
internal QueryItem(Entity entity, ComponentPool<T0> pool0)
|
||||
{
|
||||
_entity = entity;
|
||||
_pool0 = pool0;
|
||||
}
|
||||
|
||||
public Entity Entity => _entity;
|
||||
|
||||
public ref T0 Component0 => ref _pool0.GetRef(_entity);
|
||||
|
||||
// Deconstruct into tuple-like values
|
||||
public void Deconstruct(out Entity entity, out CompRef<T0> c0)
|
||||
{
|
||||
entity = _entity;
|
||||
|
||||
c0 = new (ref _pool0.GetRef(_entity));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct QueryItem<T0, T1>
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData
|
||||
{
|
||||
private readonly Entity _entity;
|
||||
private readonly ComponentPool<T0> _pool0;
|
||||
private readonly ComponentPool<T1> _pool1;
|
||||
|
||||
internal QueryItem(Entity entity, ComponentPool<T0> pool0, ComponentPool<T1> pool1)
|
||||
{
|
||||
_entity = entity;
|
||||
_pool0 = pool0;
|
||||
_pool1 = pool1;
|
||||
}
|
||||
|
||||
public Entity Entity => _entity;
|
||||
|
||||
public ref T0 Component0 => ref _pool0.GetRef(_entity);
|
||||
public ref T1 Component1 => ref _pool1.GetRef(_entity);
|
||||
|
||||
// Deconstruct into tuple-like values
|
||||
public void Deconstruct(out Entity entity, out CompRef<T0> c0, out CompRef<T1> c1)
|
||||
{
|
||||
entity = _entity;
|
||||
|
||||
c0 = new (ref _pool0.GetRef(_entity));
|
||||
c1 = new (ref _pool1.GetRef(_entity));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct QueryItem<T0, T1, T2>
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData
|
||||
{
|
||||
private readonly Entity _entity;
|
||||
private readonly ComponentPool<T0> _pool0;
|
||||
private readonly ComponentPool<T1> _pool1;
|
||||
private readonly ComponentPool<T2> _pool2;
|
||||
|
||||
internal QueryItem(Entity entity, ComponentPool<T0> pool0, ComponentPool<T1> pool1, ComponentPool<T2> pool2)
|
||||
{
|
||||
_entity = entity;
|
||||
_pool0 = pool0;
|
||||
_pool1 = pool1;
|
||||
_pool2 = pool2;
|
||||
}
|
||||
|
||||
public Entity Entity => _entity;
|
||||
|
||||
public ref T0 Component0 => ref _pool0.GetRef(_entity);
|
||||
public ref T1 Component1 => ref _pool1.GetRef(_entity);
|
||||
public ref T2 Component2 => ref _pool2.GetRef(_entity);
|
||||
|
||||
// Deconstruct into tuple-like values
|
||||
public void Deconstruct(out Entity entity, out CompRef<T0> c0, out CompRef<T1> c1, out CompRef<T2> c2)
|
||||
{
|
||||
entity = _entity;
|
||||
|
||||
c0 = new (ref _pool0.GetRef(_entity));
|
||||
c1 = new (ref _pool1.GetRef(_entity));
|
||||
c2 = new (ref _pool2.GetRef(_entity));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct QueryItem<T0, T1, T2, T3>
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData
|
||||
{
|
||||
private readonly Entity _entity;
|
||||
private readonly ComponentPool<T0> _pool0;
|
||||
private readonly ComponentPool<T1> _pool1;
|
||||
private readonly ComponentPool<T2> _pool2;
|
||||
private readonly ComponentPool<T3> _pool3;
|
||||
|
||||
internal QueryItem(Entity entity, ComponentPool<T0> pool0, ComponentPool<T1> pool1, ComponentPool<T2> pool2, ComponentPool<T3> pool3)
|
||||
{
|
||||
_entity = entity;
|
||||
_pool0 = pool0;
|
||||
_pool1 = pool1;
|
||||
_pool2 = pool2;
|
||||
_pool3 = pool3;
|
||||
}
|
||||
|
||||
public Entity Entity => _entity;
|
||||
|
||||
public ref T0 Component0 => ref _pool0.GetRef(_entity);
|
||||
public ref T1 Component1 => ref _pool1.GetRef(_entity);
|
||||
public ref T2 Component2 => ref _pool2.GetRef(_entity);
|
||||
public ref T3 Component3 => ref _pool3.GetRef(_entity);
|
||||
|
||||
// Deconstruct into tuple-like values
|
||||
public void Deconstruct(out Entity entity, out CompRef<T0> c0, out CompRef<T1> c1, out CompRef<T2> c2, out CompRef<T3> c3)
|
||||
{
|
||||
entity = _entity;
|
||||
|
||||
c0 = new (ref _pool0.GetRef(_entity));
|
||||
c1 = new (ref _pool1.GetRef(_entity));
|
||||
c2 = new (ref _pool2.GetRef(_entity));
|
||||
c3 = new (ref _pool3.GetRef(_entity));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct QueryItem<T0, T1, T2, T3, T4>
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData
|
||||
{
|
||||
private readonly Entity _entity;
|
||||
private readonly ComponentPool<T0> _pool0;
|
||||
private readonly ComponentPool<T1> _pool1;
|
||||
private readonly ComponentPool<T2> _pool2;
|
||||
private readonly ComponentPool<T3> _pool3;
|
||||
private readonly ComponentPool<T4> _pool4;
|
||||
|
||||
internal QueryItem(Entity entity, ComponentPool<T0> pool0, ComponentPool<T1> pool1, ComponentPool<T2> pool2, ComponentPool<T3> pool3, ComponentPool<T4> pool4)
|
||||
{
|
||||
_entity = entity;
|
||||
_pool0 = pool0;
|
||||
_pool1 = pool1;
|
||||
_pool2 = pool2;
|
||||
_pool3 = pool3;
|
||||
_pool4 = pool4;
|
||||
}
|
||||
|
||||
public Entity Entity => _entity;
|
||||
|
||||
public ref T0 Component0 => ref _pool0.GetRef(_entity);
|
||||
public ref T1 Component1 => ref _pool1.GetRef(_entity);
|
||||
public ref T2 Component2 => ref _pool2.GetRef(_entity);
|
||||
public ref T3 Component3 => ref _pool3.GetRef(_entity);
|
||||
public ref T4 Component4 => ref _pool4.GetRef(_entity);
|
||||
|
||||
// Deconstruct into tuple-like values
|
||||
public void Deconstruct(out Entity entity, out CompRef<T0> c0, out CompRef<T1> c1, out CompRef<T2> c2, out CompRef<T3> c3, out CompRef<T4> c4)
|
||||
{
|
||||
entity = _entity;
|
||||
|
||||
c0 = new (ref _pool0.GetRef(_entity));
|
||||
c1 = new (ref _pool1.GetRef(_entity));
|
||||
c2 = new (ref _pool2.GetRef(_entity));
|
||||
c3 = new (ref _pool3.GetRef(_entity));
|
||||
c4 = new (ref _pool4.GetRef(_entity));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct QueryItem<T0, T1, T2, T3, T4, T5>
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData
|
||||
{
|
||||
private readonly Entity _entity;
|
||||
private readonly ComponentPool<T0> _pool0;
|
||||
private readonly ComponentPool<T1> _pool1;
|
||||
private readonly ComponentPool<T2> _pool2;
|
||||
private readonly ComponentPool<T3> _pool3;
|
||||
private readonly ComponentPool<T4> _pool4;
|
||||
private readonly ComponentPool<T5> _pool5;
|
||||
|
||||
internal QueryItem(Entity entity, ComponentPool<T0> pool0, ComponentPool<T1> pool1, ComponentPool<T2> pool2, ComponentPool<T3> pool3, ComponentPool<T4> pool4, ComponentPool<T5> pool5)
|
||||
{
|
||||
_entity = entity;
|
||||
_pool0 = pool0;
|
||||
_pool1 = pool1;
|
||||
_pool2 = pool2;
|
||||
_pool3 = pool3;
|
||||
_pool4 = pool4;
|
||||
_pool5 = pool5;
|
||||
}
|
||||
|
||||
public Entity Entity => _entity;
|
||||
|
||||
public ref T0 Component0 => ref _pool0.GetRef(_entity);
|
||||
public ref T1 Component1 => ref _pool1.GetRef(_entity);
|
||||
public ref T2 Component2 => ref _pool2.GetRef(_entity);
|
||||
public ref T3 Component3 => ref _pool3.GetRef(_entity);
|
||||
public ref T4 Component4 => ref _pool4.GetRef(_entity);
|
||||
public ref T5 Component5 => ref _pool5.GetRef(_entity);
|
||||
|
||||
// Deconstruct into tuple-like values
|
||||
public void Deconstruct(out Entity entity, out CompRef<T0> c0, out CompRef<T1> c1, out CompRef<T2> c2, out CompRef<T3> c3, out CompRef<T4> c4, out CompRef<T5> c5)
|
||||
{
|
||||
entity = _entity;
|
||||
|
||||
c0 = new (ref _pool0.GetRef(_entity));
|
||||
c1 = new (ref _pool1.GetRef(_entity));
|
||||
c2 = new (ref _pool2.GetRef(_entity));
|
||||
c3 = new (ref _pool3.GetRef(_entity));
|
||||
c4 = new (ref _pool4.GetRef(_entity));
|
||||
c5 = new (ref _pool5.GetRef(_entity));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct QueryItem<T0, T1, T2, T3, T4, T5, T6>
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData where T6 : unmanaged, IComponentData
|
||||
{
|
||||
private readonly Entity _entity;
|
||||
private readonly ComponentPool<T0> _pool0;
|
||||
private readonly ComponentPool<T1> _pool1;
|
||||
private readonly ComponentPool<T2> _pool2;
|
||||
private readonly ComponentPool<T3> _pool3;
|
||||
private readonly ComponentPool<T4> _pool4;
|
||||
private readonly ComponentPool<T5> _pool5;
|
||||
private readonly ComponentPool<T6> _pool6;
|
||||
|
||||
internal QueryItem(Entity entity, ComponentPool<T0> pool0, ComponentPool<T1> pool1, ComponentPool<T2> pool2, ComponentPool<T3> pool3, ComponentPool<T4> pool4, ComponentPool<T5> pool5, ComponentPool<T6> pool6)
|
||||
{
|
||||
_entity = entity;
|
||||
_pool0 = pool0;
|
||||
_pool1 = pool1;
|
||||
_pool2 = pool2;
|
||||
_pool3 = pool3;
|
||||
_pool4 = pool4;
|
||||
_pool5 = pool5;
|
||||
_pool6 = pool6;
|
||||
}
|
||||
|
||||
public Entity Entity => _entity;
|
||||
|
||||
public ref T0 Component0 => ref _pool0.GetRef(_entity);
|
||||
public ref T1 Component1 => ref _pool1.GetRef(_entity);
|
||||
public ref T2 Component2 => ref _pool2.GetRef(_entity);
|
||||
public ref T3 Component3 => ref _pool3.GetRef(_entity);
|
||||
public ref T4 Component4 => ref _pool4.GetRef(_entity);
|
||||
public ref T5 Component5 => ref _pool5.GetRef(_entity);
|
||||
public ref T6 Component6 => ref _pool6.GetRef(_entity);
|
||||
|
||||
// Deconstruct into tuple-like values
|
||||
public void Deconstruct(out Entity entity, out CompRef<T0> c0, out CompRef<T1> c1, out CompRef<T2> c2, out CompRef<T3> c3, out CompRef<T4> c4, out CompRef<T5> c5, out CompRef<T6> c6)
|
||||
{
|
||||
entity = _entity;
|
||||
|
||||
c0 = new (ref _pool0.GetRef(_entity));
|
||||
c1 = new (ref _pool1.GetRef(_entity));
|
||||
c2 = new (ref _pool2.GetRef(_entity));
|
||||
c3 = new (ref _pool3.GetRef(_entity));
|
||||
c4 = new (ref _pool4.GetRef(_entity));
|
||||
c5 = new (ref _pool5.GetRef(_entity));
|
||||
c6 = new (ref _pool6.GetRef(_entity));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct QueryItem<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData where T6 : unmanaged, IComponentData where T7 : unmanaged, IComponentData
|
||||
{
|
||||
private readonly Entity _entity;
|
||||
private readonly ComponentPool<T0> _pool0;
|
||||
private readonly ComponentPool<T1> _pool1;
|
||||
private readonly ComponentPool<T2> _pool2;
|
||||
private readonly ComponentPool<T3> _pool3;
|
||||
private readonly ComponentPool<T4> _pool4;
|
||||
private readonly ComponentPool<T5> _pool5;
|
||||
private readonly ComponentPool<T6> _pool6;
|
||||
private readonly ComponentPool<T7> _pool7;
|
||||
|
||||
internal QueryItem(Entity entity, ComponentPool<T0> pool0, ComponentPool<T1> pool1, ComponentPool<T2> pool2, ComponentPool<T3> pool3, ComponentPool<T4> pool4, ComponentPool<T5> pool5, ComponentPool<T6> pool6, ComponentPool<T7> pool7)
|
||||
{
|
||||
_entity = entity;
|
||||
_pool0 = pool0;
|
||||
_pool1 = pool1;
|
||||
_pool2 = pool2;
|
||||
_pool3 = pool3;
|
||||
_pool4 = pool4;
|
||||
_pool5 = pool5;
|
||||
_pool6 = pool6;
|
||||
_pool7 = pool7;
|
||||
}
|
||||
|
||||
public Entity Entity => _entity;
|
||||
|
||||
public ref T0 Component0 => ref _pool0.GetRef(_entity);
|
||||
public ref T1 Component1 => ref _pool1.GetRef(_entity);
|
||||
public ref T2 Component2 => ref _pool2.GetRef(_entity);
|
||||
public ref T3 Component3 => ref _pool3.GetRef(_entity);
|
||||
public ref T4 Component4 => ref _pool4.GetRef(_entity);
|
||||
public ref T5 Component5 => ref _pool5.GetRef(_entity);
|
||||
public ref T6 Component6 => ref _pool6.GetRef(_entity);
|
||||
public ref T7 Component7 => ref _pool7.GetRef(_entity);
|
||||
|
||||
// Deconstruct into tuple-like values
|
||||
public void Deconstruct(out Entity entity, out CompRef<T0> c0, out CompRef<T1> c1, out CompRef<T2> c2, out CompRef<T3> c3, out CompRef<T4> c4, out CompRef<T5> c5, out CompRef<T6> c6, out CompRef<T7> c7)
|
||||
{
|
||||
entity = _entity;
|
||||
|
||||
c0 = new (ref _pool0.GetRef(_entity));
|
||||
c1 = new (ref _pool1.GetRef(_entity));
|
||||
c2 = new (ref _pool2.GetRef(_entity));
|
||||
c3 = new (ref _pool3.GetRef(_entity));
|
||||
c4 = new (ref _pool4.GetRef(_entity));
|
||||
c5 = new (ref _pool5.GetRef(_entity));
|
||||
c6 = new (ref _pool6.GetRef(_entity));
|
||||
c7 = new (ref _pool7.GetRef(_entity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<#@ template language="C#" debug="false" hostspecific="true" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ include file="Helpers.ttinclude" #>
|
||||
<#@ output extension=".cs" #>
|
||||
|
||||
using Ghost.Entities.Components;
|
||||
using Ghost.Entities.Query;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
<# for (int arity = 1; arity <= Amount; arity++)
|
||||
{
|
||||
var generics = AppendGenerics(arity);
|
||||
var restrictions = AppendGenericRestrictions(arity, "unmanaged, IComponentData");
|
||||
var constructorParams = Enumerable.Range(0, arity)
|
||||
.Select(i => $"ComponentPool<T{i}> pool{i}")
|
||||
.Aggregate((a, b) => a + ", " + b);
|
||||
var deconstructParams = Enumerable.Range(0, arity)
|
||||
.Select(i => {
|
||||
var name = $"c{i}";
|
||||
return arity == 1
|
||||
? $"out CompRef<T0> {name}"
|
||||
: $"out CompRef<T{i}> {name}";
|
||||
})
|
||||
.Aggregate((a, b) => a + ", " + b);
|
||||
#>
|
||||
public readonly struct QueryItem<<#= generics #>>
|
||||
<#= restrictions #>
|
||||
{
|
||||
private readonly Entity _entity;
|
||||
<# for (int i = 0; i < arity; i++){ #>
|
||||
private readonly ComponentPool<T<#= i #>> _pool<#= i #>;
|
||||
<# } #>
|
||||
|
||||
internal QueryItem(Entity entity, <#= constructorParams #>)
|
||||
{
|
||||
_entity = entity;
|
||||
<# for (int i = 0; i < arity; i++){ #>
|
||||
_pool<#= i #> = pool<#= i #>;
|
||||
<# } #>
|
||||
}
|
||||
|
||||
public Entity Entity => _entity;
|
||||
|
||||
<# for (int i = 0; i < arity; i++){ #>
|
||||
public ref T<#= i #> Component<#= i #> => ref _pool<#= i #>.GetRef(_entity);
|
||||
<# } #>
|
||||
|
||||
// Deconstruct into tuple-like values
|
||||
public void Deconstruct(out Entity entity, <#= deconstructParams #>)
|
||||
{
|
||||
entity = _entity;
|
||||
|
||||
<# for (int i = 0; i < arity; i++){ #>
|
||||
c<#= i #> = new (ref _pool<#= i #>.GetRef(_entity));
|
||||
<# } #>
|
||||
}
|
||||
}
|
||||
|
||||
<# } #>
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
|
||||
using Ghost.SparseEntities.Components;
|
||||
|
||||
namespace Ghost.SparseEntities;
|
||||
|
||||
public delegate void QueryRefComponent<T0>(Entity entity, ref T0 t0Component)
|
||||
where T0 : unmanaged, IComponentData;
|
||||
public delegate void QueryRefComponent<T0, T1>(Entity entity, ref T0 t0Component,ref T1 t1Component)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData;
|
||||
public delegate void QueryRefComponent<T0, T1, T2>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData;
|
||||
public delegate void QueryRefComponent<T0, T1, T2, T3>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData;
|
||||
public delegate void QueryRefComponent<T0, T1, T2, T3, T4>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData;
|
||||
public delegate void QueryRefComponent<T0, T1, T2, T3, T4, T5>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData;
|
||||
public delegate void QueryRefComponent<T0, T1, T2, T3, T4, T5, T6>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData where T6 : unmanaged, IComponentData;
|
||||
public delegate void QueryRefComponent<T0, T1, T2, T3, T4, T5, T6, T7>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component,ref T7 t7Component)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData where T6 : unmanaged, IComponentData where T7 : unmanaged, IComponentData;
|
||||
@@ -1,21 +0,0 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ include file="Helpers.ttinclude" #>
|
||||
|
||||
using Ghost.Entities.Components;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
<#
|
||||
for (var index = 1; index <= Amount; index++)
|
||||
{
|
||||
var generics = AppendGenerics(index);
|
||||
var parameters = AppendGenericRefParameters(index);
|
||||
var restrictions = AppendGenericRestrictions(index, "unmanaged, IComponentData");
|
||||
#>
|
||||
public delegate void QueryRefComponent<<#= generics #>>(Entity entity, <#= parameters.ToString() #>)
|
||||
<#= restrictions.ToString() #>;
|
||||
<#
|
||||
}
|
||||
#>
|
||||
@@ -1,242 +0,0 @@
|
||||
|
||||
|
||||
using Ghost.SparseEntities.Components;
|
||||
using Ghost.SparseEntities.Query;
|
||||
|
||||
namespace Ghost.SparseEntities;
|
||||
|
||||
public partial class World
|
||||
{
|
||||
public QueryEnumerable<T0> Query<T0>()
|
||||
where T0 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0>(
|
||||
this,
|
||||
pool0,
|
||||
pool0.Count);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0> QueryFilter<T0>(ref readonly QueryFilter filter)
|
||||
where T0 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0>(
|
||||
this,
|
||||
pool0,
|
||||
pool0.Count,
|
||||
in filter);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1> Query<T0, T1>()
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1>(
|
||||
this,
|
||||
pool0, pool1,
|
||||
pool0.Count);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1> QueryFilter<T0, T1>(ref readonly QueryFilter filter)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1>(
|
||||
this,
|
||||
pool0, pool1,
|
||||
pool0.Count,
|
||||
in filter);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2> Query<T0, T1, T2>()
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2>(
|
||||
this,
|
||||
pool0, pool1, pool2,
|
||||
pool0.Count);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2> QueryFilter<T0, T1, T2>(ref readonly QueryFilter filter)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2>(
|
||||
this,
|
||||
pool0, pool1, pool2,
|
||||
pool0.Count,
|
||||
in filter);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3> Query<T0, T1, T2, T3>()
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3,
|
||||
pool0.Count);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3> QueryFilter<T0, T1, T2, T3>(ref readonly QueryFilter filter)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3,
|
||||
pool0.Count,
|
||||
in filter);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3, T4> Query<T0, T1, T2, T3, T4>()
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3) && _componentStorage.TryGetPool<T4>(out var pool4)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3, T4>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3, pool4,
|
||||
pool0.Count);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3, T4> QueryFilter<T0, T1, T2, T3, T4>(ref readonly QueryFilter filter)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3) && _componentStorage.TryGetPool<T4>(out var pool4)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3, T4>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3, pool4,
|
||||
pool0.Count,
|
||||
in filter);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3, T4, T5> Query<T0, T1, T2, T3, T4, T5>()
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3) && _componentStorage.TryGetPool<T4>(out var pool4) && _componentStorage.TryGetPool<T5>(out var pool5)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3, T4, T5>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3, pool4, pool5,
|
||||
pool0.Count);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3, T4, T5> QueryFilter<T0, T1, T2, T3, T4, T5>(ref readonly QueryFilter filter)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3) && _componentStorage.TryGetPool<T4>(out var pool4) && _componentStorage.TryGetPool<T5>(out var pool5)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3, T4, T5>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3, pool4, pool5,
|
||||
pool0.Count,
|
||||
in filter);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3, T4, T5, T6> Query<T0, T1, T2, T3, T4, T5, T6>()
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData where T6 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3) && _componentStorage.TryGetPool<T4>(out var pool4) && _componentStorage.TryGetPool<T5>(out var pool5) && _componentStorage.TryGetPool<T6>(out var pool6)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3, T4, T5, T6>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3, pool4, pool5, pool6,
|
||||
pool0.Count);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3, T4, T5, T6> QueryFilter<T0, T1, T2, T3, T4, T5, T6>(ref readonly QueryFilter filter)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData where T6 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3) && _componentStorage.TryGetPool<T4>(out var pool4) && _componentStorage.TryGetPool<T5>(out var pool5) && _componentStorage.TryGetPool<T6>(out var pool6)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3, T4, T5, T6>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3, pool4, pool5, pool6,
|
||||
pool0.Count,
|
||||
in filter);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3, T4, T5, T6, T7> Query<T0, T1, T2, T3, T4, T5, T6, T7>()
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData where T6 : unmanaged, IComponentData where T7 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3) && _componentStorage.TryGetPool<T4>(out var pool4) && _componentStorage.TryGetPool<T5>(out var pool5) && _componentStorage.TryGetPool<T6>(out var pool6) && _componentStorage.TryGetPool<T7>(out var pool7)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3, T4, T5, T6, T7>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3, pool4, pool5, pool6, pool7,
|
||||
pool0.Count);
|
||||
}
|
||||
|
||||
public QueryEnumerable<T0, T1, T2, T3, T4, T5, T6, T7> QueryFilter<T0, T1, T2, T3, T4, T5, T6, T7>(ref readonly QueryFilter filter)
|
||||
where T0 : unmanaged, IComponentData where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData where T3 : unmanaged, IComponentData where T4 : unmanaged, IComponentData where T5 : unmanaged, IComponentData where T6 : unmanaged, IComponentData where T7 : unmanaged, IComponentData
|
||||
{
|
||||
if (!(_componentStorage.TryGetPool<T0>(out var pool0) && _componentStorage.TryGetPool<T1>(out var pool1) && _componentStorage.TryGetPool<T2>(out var pool2) && _componentStorage.TryGetPool<T3>(out var pool3) && _componentStorage.TryGetPool<T4>(out var pool4) && _componentStorage.TryGetPool<T5>(out var pool5) && _componentStorage.TryGetPool<T6>(out var pool6) && _componentStorage.TryGetPool<T7>(out var pool7)))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<T0, T1, T2, T3, T4, T5, T6, T7>(
|
||||
this,
|
||||
pool0, pool1, pool2, pool3, pool4, pool5, pool6, pool7,
|
||||
pool0.Count,
|
||||
in filter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ include file="Helpers.ttinclude" #>
|
||||
|
||||
using Ghost.Entities.Components;
|
||||
using Ghost.Entities.Query;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
public partial class World
|
||||
{
|
||||
<# for (var index = 1; index <= Amount; index++) {
|
||||
var generics = AppendGenerics(index);
|
||||
var restrictions = AppendGenericRestrictions(index, "unmanaged, IComponentData");
|
||||
var tryGetPools = TryGetComponentPools(index);
|
||||
var poolParams = Enumerable.Range(0, index)
|
||||
.Select(i => $"pool{i}")
|
||||
.Aggregate((a,b)=> a + ", " + b);
|
||||
var countSource = "pool0.Count";
|
||||
#>
|
||||
public QueryEnumerable<<#= generics #>> Query<<#= generics #>>()
|
||||
<#= restrictions #>
|
||||
{
|
||||
if (!(<#= tryGetPools #>))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<<#= generics #>>(
|
||||
this,
|
||||
<#= poolParams #>,
|
||||
<#= countSource #>);
|
||||
}
|
||||
|
||||
public QueryEnumerable<<#= generics #>> QueryFilter<<#= generics #>>(ref readonly QueryFilter filter)
|
||||
<#= restrictions #>
|
||||
{
|
||||
if (!(<#= tryGetPools #>))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new QueryEnumerable<<#= generics #>>(
|
||||
this,
|
||||
<#= poolParams #>,
|
||||
<#= countSource #>,
|
||||
in filter);
|
||||
}
|
||||
|
||||
<# } #>
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
using Ghost.SparseEntities.Components;
|
||||
using Ghost.SparseEntities.Query;
|
||||
using Ghost.SparseEntities.Systems;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.SparseEntities;
|
||||
|
||||
// TODO: Archetype system for better performance
|
||||
public partial class World
|
||||
{
|
||||
private static List<World> s_worlds = new(4);
|
||||
private static Queue<WorldID> s_freeWorldSlots = new();
|
||||
|
||||
public static int WorldCount => s_worlds.Count - s_freeWorldSlots.Count;
|
||||
|
||||
public static World Create(int entityCapacity = 16)
|
||||
{
|
||||
lock (s_worlds)
|
||||
{
|
||||
if (s_freeWorldSlots.TryDequeue(out var index))
|
||||
{
|
||||
s_worlds[index] = new World(index, entityCapacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_worlds.Count >= WorldID.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException("Maximum number of worlds reached");
|
||||
}
|
||||
|
||||
index = (WorldID)s_worlds.Count;
|
||||
s_worlds.Add(new World(index, entityCapacity));
|
||||
}
|
||||
|
||||
return s_worlds[index];
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static World GetWorld(int index)
|
||||
{
|
||||
return s_worlds[index];
|
||||
}
|
||||
}
|
||||
|
||||
public partial class World : IDisposable, IEquatable<World>
|
||||
{
|
||||
private readonly WorldID _id;
|
||||
private readonly EntityManager _entityManager;
|
||||
private readonly ComponentStorage _componentStorage;
|
||||
private readonly SystemStorage _systemStorage;
|
||||
|
||||
private bool _isDisposed = false;
|
||||
|
||||
internal ComponentStorage ComponentStorage => _componentStorage;
|
||||
|
||||
public WorldID ID => _id;
|
||||
public EntityManager EntityManager => _entityManager;
|
||||
public SystemStorage SystemStorage => _systemStorage;
|
||||
|
||||
public event Action<World, Entity, Type>? ComponentChanged;
|
||||
|
||||
private World(WorldID id, int entityCapacity)
|
||||
{
|
||||
_id = id;
|
||||
_entityManager = new EntityManager(this, entityCapacity);
|
||||
_componentStorage = new ComponentStorage(this);
|
||||
_systemStorage = new SystemStorage(this);
|
||||
}
|
||||
|
||||
~World()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public CompRef<T> GetSingleton<T>()
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
ref var component = ref CollectionsMarshal.GetValueRefOrAddDefault(SingletonContainer<T>.container, _id, out _);
|
||||
return new CompRef<T>(ref component);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IEnumerable<ScriptComponent> QueryScript()
|
||||
{
|
||||
if (_componentStorage.ScriptComponentPool.IsInitialized)
|
||||
{
|
||||
return _componentStorage.ScriptComponentPool.ExecutionList!;
|
||||
}
|
||||
|
||||
return Enumerable.Empty<ScriptComponent>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Conditional("GHOST_EDITOR")]
|
||||
public void NotifyComponentChanged(Entity entity, Type type)
|
||||
{
|
||||
ComponentChanged?.Invoke(this, entity, type);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Conditional("GHOST_EDITOR")]
|
||||
public void NotifyComponentChanged<T>(Entity entity)
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
NotifyComponentChanged(entity, typeof(T));
|
||||
}
|
||||
|
||||
public bool Equals(World? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _id == other._id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _id.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is World other && Equals(other);
|
||||
}
|
||||
|
||||
public static bool operator ==(World? left, World? right)
|
||||
{
|
||||
return left?.Equals(right) ?? right is null;
|
||||
}
|
||||
|
||||
public static bool operator !=(World? left, World? right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_entityManager.Dispose();
|
||||
_componentStorage.Dispose();
|
||||
_systemStorage.Dispose();
|
||||
|
||||
s_freeWorldSlots.Enqueue(_id);
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user