Files
GhostEngine/Ghost.Entities.Test/EntityTest.cs
Misaki 856fa4f07d Per-component versioning and change tracking for ECS
Introduce per-component versioning in chunks and world for efficient change detection.
- Add version arrays to chunks and global version to world.
- Update queries and ForEach to mark written components as changed.
- Extend QueryBuilder with WithAllRW/WithPresentRW for write access.
- Expose change tracking API in ChunkView.
- Improve thread safety and debug code.
- Update tests and examples to demonstrate new features.
2025-12-10 19:01:25 +09:00

115 lines
3.5 KiB
C#

using Ghost.Test.Core;
using Misaki.HighPerformance.Jobs;
using Misaki.HighPerformance.Mathematics;
namespace Ghost.Entities.Test;
internal struct TestEntityQueryJob : IJobChunk
{
public void Execute(ChunkView view, int threadIndex)
{
var transforms = view.GetComponentData<Transform>();
for (var i = 0; i < view.Count; i++)
{
transforms[i].position += new float3(10, 10, 10);
}
}
}
public partial class EntityTest : 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, ComponentTypeID<ManagedEntityRef>.value);
_world.EntityManager.AddComponent(entity1, new Mesh { index = 1 });
_world.EntityManager.SetComponent(entity1, new ManagedEntityRef
{
entity = _world.EntityManager.CreateManagedEntity()
});
var entity2 = _world.EntityManager.CreateEntity(ComponentTypeID<Transform>.value);
_world.EntityManager.SetComponent(entity2, new Transform { position = new float3(1, 2, 3) });
var queryID = new QueryBuilder().WithAllRW<Transform>().WithAbsent<Mesh>().Build(_world);
ref var query = ref _world.GetEntityQueryReference(queryID);
// var testJob = new TestEntityQueryJob();
// var handle = query.ScheduleChunkParallel(testJob, 64, JobHandle.Invalid);
// _jobScheduler.WaitComplete(handle);
_world.EntityManager.AddScriptComponent<TestScriptComponent>(entity1);
_world.EntityManager.RemoveComponent<ManagedEntityRef>(entity1); // This should destory the managed entity and call OnDestroy
_world.AdvanceVersion();
query.ForEach<Transform>((e, ref t) =>
{
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}");
//}
foreach (var chunk in query.GetChunkIterator())
{
var transforms = chunk.GetComponentData<Transform>();
var entities = chunk.GetEntities();
var bits = chunk.GetEnableBits<Transform>();
var changed = chunk.HasChanged<Transform>(0);
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.DestroyEntity(entity1);
_world.EntityManager.DestroyEntity(entity2);
}
public void Cleanup()
{
_world.Dispose();
_jobScheduler.Dispose();
JobScheduler.ReleaseTempAllocator();
}
}
public struct Transform : IEnableableComponent
{
public float3 position;
}
public struct Mesh : IComponent
{
public int index;
}
public class TestScriptComponent : ScriptComponent
{
public override void OnCreate()
{
Console.WriteLine($"TestScriptComponent OnCreate called for Entity {Entity}");
ref var transform = ref GetComponent<Transform>();
transform.position += new float3(0, 1, 0);
}
public override void OnDestroy()
{
Console.WriteLine($"TestScriptComponent OnDestroy called for Entity {Entity}");
}
}