ECS refactor: new ComponentSet, serialization, generators

Major ECS API overhaul: added ComponentSet, refactored ComponentRegistry, and updated all entity/component creation methods. Introduced robust custom serialization infrastructure and per-component source generators for registration and (de)serialization. Updated editor, engine, and test code to use new APIs. Improved code quality, naming, and performance throughout. Removed obsolete code and updated dependencies.
This commit is contained in:
2025-12-20 20:41:40 +09:00
parent 3118021272
commit 00b4e82ded
60 changed files with 1216 additions and 814 deletions

View File

@@ -1,4 +1,5 @@
using Ghost.Core;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Runtime.CompilerServices;
@@ -16,26 +17,88 @@ public interface IEnableableComponent : IComponent
internal struct ComponentInfo
{
// public string stableName; // Do we actually need this?
public int id;
public Identifier<IComponent> id;
public int size;
public int alignment;
public bool isEnableable;
}
/// <summary>
/// Represents an immutable set of component identifiers used to define a group of components within an entity or system.
/// </summary>
public struct ComponentSet : IDisposable, IEquatable<ComponentSet>
{
private UnsafeArray<Identifier<IComponent>> _components;
private int _hashCode;
public readonly ReadOnlySpan<Identifier<IComponent>> Components => _components.AsSpan();
public ComponentSet(AllocationHandle allocationHandle, params ReadOnlySpan<Identifier<IComponent>> components)
{
_components = new UnsafeArray<Identifier<IComponent>>(components.Length, allocationHandle);
components.CopyTo(_components.AsSpan());
_hashCode = -1;
}
public ComponentSet(Allocator allocator, params ReadOnlySpan<Identifier<IComponent>> components)
: this(AllocationManager.GetAllocationHandle(allocator), components)
{
}
public readonly bool Equals(ComponentSet other)
{
return _hashCode == other._hashCode;
}
public override int GetHashCode()
{
if (_hashCode == -1)
{
_hashCode = ComponentRegistry.GetHashCode(_components.AsSpan());
}
return _hashCode;
}
public override bool Equals(object? obj)
{
return obj is ComponentSet set && Equals(set);
}
public static bool operator ==(ComponentSet left, ComponentSet right)
{
return left.Equals(right);
}
public static bool operator !=(ComponentSet left, ComponentSet right)
{
return !(left == right);
}
public void Dispose()
{
_components.Dispose();
}
}
/// <summary>
/// Provides a unique identifier for the specified unmanaged component type.
/// </summary>
/// <typeparam name="T">The component type for which to obtain an identifier. Must be unmanaged and implement <see cref="IComponent"/>.</typeparam>
public static class ComponentTypeID<T>
where T : unmanaged, IComponent
{
public static readonly Identifier<IComponent> value = ComponentRegister.GetOrRegisterComponent<T>();
public static readonly Identifier<IComponent> Value = ComponentRegistry.GetOrRegisterComponent<T>();
}
internal static class ComponentRegister
internal static class ComponentRegistry
{
private static readonly List<ComponentInfo> s_registeredComponents = new();
private static readonly Dictionary<IntPtr, int> s_typeHandleToID = new();
private static readonly Dictionary<string, int> s_nameToRuntimeID = new();
#if DEBUG || GHOST_EDITOR
internal static readonly Dictionary<int, Type> s_runtimeIDToType = new();
#endif
public static unsafe Identifier<IComponent> GetOrRegisterComponent<T>()
where T : unmanaged, IComponent
@@ -66,9 +129,7 @@ internal static class ComponentRegister
s_typeHandleToID[typeHandle] = newID;
s_nameToRuntimeID[stableName] = newID;
#if DEBUG || GHOST_EDITOR
s_runtimeIDToType[newID.value] = typeof(T);
#endif
return newID;
}
@@ -98,7 +159,21 @@ internal static class ComponentRegister
}
}
// TODO: A ComponentSet structure to cache the hashcode for better performance.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ComponentInfo GetComponentInfo(Type type)
{
lock (s_registeredComponents)
{
var typeId = GetComponentID(type);
if (typeId.IsInvalid)
{
throw new KeyNotFoundException($"Component type {type.FullName} is not registered.");
}
return s_registeredComponents[typeId];
}
}
public static int GetHashCode(params ReadOnlySpan<Identifier<IComponent>> componentTypeIDs)
{
var largestID = 0;