diff --git a/BITKit.csproj b/BITKit.csproj
index 1743760..db5ee62 100644
--- a/BITKit.csproj
+++ b/BITKit.csproj
@@ -5,6 +5,7 @@
disable
BITKit
net7.0
+ true
@@ -51,6 +52,7 @@
+
diff --git a/Src/Core/ECS/EntitiesService.cs b/Src/Core/ECS/EntitiesService.cs
index 1325d65..7c059df 100644
--- a/Src/Core/ECS/EntitiesService.cs
+++ b/Src/Core/ECS/EntitiesService.cs
@@ -1,284 +1,623 @@
using System;
+using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Threading;
+using kcp2k;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Unity.Mathematics;
namespace BITKit.Entities
{
- public class EntitiesService:IEntitiesService,IDisposable
+ public static class EntitiesServiceExtensions
{
- private readonly ILogger _logger;
- private readonly IFixedTicker _ticker;
- private static int _count;
- private static readonly ConcurrentQueue OnAddQueue = new();
- private static ConcurrentDictionary> TypeCaches = new();
- public EntitiesService(ILogger logger, IFixedTicker ticker)
+ internal static T GetRequiredServiceWithId(this IServiceProvider serviceProvider, int id)
{
- if (_count > 0)
- {
- throw new MulticastNotSupportedException();
- }
- _count++;
+ return serviceProvider.GetRequiredService();
+ }
+ }
+ public unsafe class EntitiesService : IEntitiesService, IDisposable
+ {
+ private static int _count;
+
+
+ private readonly ILogger _logger;
+ private readonly ITicker _ticker;
+
+ private readonly ConcurrentQueue _onAddQueue = new();
+ private readonly Dictionary> _typeCaches = new();
+ private readonly Dictionary> _typeInstances = new();
+
+ private readonly ConcurrentDictionary _staticCaches = new();
+
+ public EntitiesService(ILogger logger, ITicker ticker)
+ {
_logger = logger;
_ticker = ticker;
-
- _ticker.Add(OnTick);
+
+ _pool = new(ObjectGenerator, null, 64);
+
+ _count++;
+
+ logger.LogInformation($"已创建EntitiesService,当前有:{_count}个实例");
+
+ _ticker?.Add(OnTick);
}
private void OnTick(float obj)
{
- while (OnAddQueue.TryDequeue(out var entity))
+ while (_onAddQueue.TryDequeue(out var entity))
{
OnAdd?.Invoke(entity);
-
- foreach (var serviceDescriptor in entity.ServiceCollection)
- {
- var typeHash = serviceDescriptor.ServiceType.GetHashCode();
- var hashSet = TypeCaches.GetOrCreate(typeHash);
- hashSet.Add(entity.Id);
- }
+ _staticCaches.Clear();
+ MakeCache(entity);
}
}
-
-
- private static readonly ConcurrentDictionary Entities = new();
+ private void MakeCache(IEntity entity)
+ {
+ foreach (var serviceDescriptor in entity.ServiceCollection)
+ {
+ var typeHash = serviceDescriptor.ServiceType.GetHashCode();
+ var hashSet = _typeCaches.GetOrCreate(typeHash);
+ hashSet.Add(entity.Id);
+ }
+ }
+
+ private readonly Dictionary _entitiesInternal = new();
public event Action OnAdd;
public event Action OnRemove;
- IReadOnlyDictionary IEntitiesService.Entities => Entities;
+ IReadOnlyDictionary IEntitiesService.Entities => _entitiesInternal;
+ private readonly Pool> _pool;
+
+ private HashSet ObjectGenerator()
+ {
+ return new HashSet(math.max(8192, _typeCaches.Count * 2));
+ }
+
public bool Register(IEntity entity)
{
- if (!Entities.TryAdd(entity.Id, entity)) return false;
- OnAddQueue.Enqueue(entity);
+ if (!_entitiesInternal.TryAdd(entity.Id, entity)) return false;
+ if (_ticker is not null)
+ {
+ _onAddQueue.Enqueue(entity);
+ }
+ else
+ {
+ _staticCaches.Clear();
+ OnAdd?.Invoke(entity);
+ MakeCache(entity);
+ }
+
+
+
return true;
}
public bool UnRegister(IEntity entity)
{
- if (!Entities.TryRemove(entity.Id, out _)) return false;
+ if (!_entitiesInternal.TryRemove(entity.Id)) return false;
OnRemove?.Invoke(entity);
foreach (var serviceDescriptor in entity.ServiceCollection)
{
var typeHash = serviceDescriptor.ServiceType.GetHashCode();
- var hashSet = TypeCaches.GetOrCreate(typeHash);
+ var hashSet = _typeCaches.GetOrCreate(typeHash);
hashSet.Remove(entity.Id);
}
+ _typeInstances.TryRemove(entity.Id);
+
+ _staticCaches.Clear();
+
return true;
}
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
private readonly CancellationTokenSource _cancellationTokenSource = new();
+
public IEntity Get(int id)
{
- return Entities[id];
+ return _entitiesInternal[id];
}
public bool TryGetEntity(int id, out IEntity entity)
{
- return Entities.TryGetValue(id, out entity);
+ return _entitiesInternal.TryGetValue(id, out entity);
}
public IEntity GetOrAdd(int id, Func factory)
{
- return Entities.GetOrAdd(id, factory);
- }
-
- public IEntity[] Query()
- {
- throw new NotImplementedException("Obsoleted");
- }
-
- public T[] QueryComponents()
- {
- var list = new List();
- foreach (var entity in Entities.Values)
+ if (_entitiesInternal.TryGetValue(id, out var current))
{
- if (entity.ServiceProvider.GetService() is { } t1)
- {
- list.Add(t1);
- }
+ _entitiesInternal.TryAdd(id, current = factory.Invoke(id));
}
- return list.ToArray();
- // return _queryCache.GetOrAdd(typeof(T), type =>
- // {
- // return _entities.Values.Where(entity => entity.TryGetComponent(out T component)).ToArray();
- // }).Cast().ToArray();
+ return current;
}
- public (T, T1)[] QueryComponents()
+ public Span QueryComponents() where T : class
{
- var list = new List<(T, T1)>();
- foreach (var entity in Entities.Values)
- {
- if (entity.ServiceProvider.GetService() is { } t1 &&
- entity.ServiceProvider.GetService() is { } t2)
- {
- list.Add((t1, t2));
- }
- }
-
- return list.ToArray();
- // List<(T, T1)> list = new();
- // foreach (var entity in _entities.Values)
- // {
- // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1))
- // list.Add((t, t1));
- // }
- // return list.ToArray();
- }
- public (T, T1, T2)[] QueryComponents()
- {
- var list = new List<(T, T1, T2)>();
- foreach (var entity in Entities.Values)
- {
- if (entity.ServiceProvider.GetService() is { } t1 &&
- entity.ServiceProvider.GetService() is { } t2 &&
- entity.ServiceProvider.GetService() is { } t3)
- {
- list.Add((t1, t2, t3));
- }
- }
-
- return list.ToArray();
- // List<(T, T1, T2)> list = new();
- // foreach (var entity in _entities.Values)
- // {
- // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2))
- // list.Add((t, t1, t2));
- // }
- // return list.ToArray();
- }
+ var pool = ArrayPool.Shared;
- public (T, T1, T2, T3)[] QueryComponents()
- {
- var list = new List<(T, T1, T2, T3)>();
- foreach (var entity in Entities.Values)
- {
- if (entity.ServiceProvider.GetService() is { } t1 &&
- entity.ServiceProvider.GetService() is { } t2 &&
- entity.ServiceProvider.GetService() is { } t3 &&
- entity.ServiceProvider.GetService() is { } t4)
- {
- list.Add((t1, t2, t3, t4));
- }
- }
-
- return list.ToArray();
- // List<(T, T1, T2, T3)> list = new();
- // foreach (var entity in _entities.Values)
- // {
- // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3))
- // list.Add((t, t1, t2, t3));
- // }
- // return list.ToArray();
- }
+ var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode());
- public (T, T1, T2, T3, T4)[] QueryComponents()
- {
- var list = new List<(T, T1, T2, T3, T4)>();
- foreach (var entity in Entities.Values)
- {
- if (entity.ServiceProvider.GetService() is { } t1 &&
- entity.ServiceProvider.GetService() is { } t2 &&
- entity.ServiceProvider.GetService() is { } t3 &&
- entity.ServiceProvider.GetService() is { } t4 &&
- entity.ServiceProvider.GetService() is { } t5)
- {
- list.Add((t1, t2, t3, t4, t5));
- }
- }
-
- return list.ToArray();
- // List<(T, T1, T2, T3, T4)> list = new();
- // foreach (var entity in _entities.Values)
- // {
- // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3) && entity.TryGetComponent(out T4 t4))
- // list.Add((t, t1, t2, t3, t4));
- // }
- // return list.ToArray();
- }
+ var array = pool.Rent(hashset.Count);
+ var collection = _pool.Take();
+ collection.Clear();
- public (T, T1, T2, T3, T4, T5)[] QueryComponents()
- {
- var list = new List<(T, T1, T2, T3, T4, T5)>();
- foreach (var entity in Entities.Values)
- {
- if (entity.ServiceProvider.GetService() is { } t1 &&
- entity.ServiceProvider.GetService() is { } t2 &&
- entity.ServiceProvider.GetService() is { } t3 &&
- entity.ServiceProvider.GetService() is { } t4 &&
- entity.ServiceProvider.GetService() is { } t5 &&
- entity.ServiceProvider.GetService() is { } t6)
- {
- list.Add((t1, t2, t3, t4, t5, t6));
- }
- }
-
- return list.ToArray();
-// List<(T, T1, T2, T3, T4, T5)> list = new();
-// foreach (var entity in _entities.Values)
-// {
-// if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3) && entity.TryGetComponent(out T4 t4) && entity.TryGetComponent(out T5 t5))
-// list.Add((t, t1, t2, t3, t4, t5));
-// }
-// return list.ToArray();
- }
+ collection.UnionWith(hashset);
- public (T, T1, T2, T3, T4, T5, T6)[] QueryComponents()
- {
- var list = new List<(T, T1, T2, T3, T4, T5, T6)>();
- foreach (var entity in Entities.Values)
+ var i = 0;
+ foreach (var id in collection)
{
- if (entity.ServiceProvider.GetService() is { } t1 &&
- entity.ServiceProvider.GetService() is { } t2 &&
- entity.ServiceProvider.GetService() is { } t3 &&
- entity.ServiceProvider.GetService() is { } t4 &&
- entity.ServiceProvider.GetService() is { } t5 &&
- entity.ServiceProvider.GetService() is { } t6 &&
- entity.ServiceProvider.GetService() is { } t7)
+ var instances = _typeInstances.GetOrCreate(id);
+
+ var h0 = typeof(T).GetHashCode();
+
+ if (instances.TryGetValue(h0, out var v0) is false)
{
- list.Add((t1, t2, t3, t4, t5, t6, t7));
+ instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
}
+
+ array[i] = Unsafe.As(v0);
+ i++;
}
- return list.ToArray();
- // List<(T, T1, T2, T3, T4, T5, T6)> list = new();
- // foreach (var entity in _entities.Values)
- // {
- // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3) && entity.TryGetComponent(out T4 t4) && entity.TryGetComponent(out T5 t5) && entity.TryGetComponent(out T6 t6))
- // list.Add((t, t1, t2, t3, t4, t5, t6));
- // }
- // return list.ToArray();
+ try
+ {
+ return new Span(array, 0, i);
+ }
+ finally
+ {
+ pool.Return(array);
+ _pool.Return(collection);
+ }
}
- public ValueTuple[] QueryComponents()
- where TRest : struct
+ public Span<(T, T1)> QueryComponents() where T : class where T1 : class
{
- var list = new List>();
- foreach (var entity in Entities.Values)
+ var pool = ArrayPool<(T, T1)>.Shared;
+
+ var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode());
+ var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode());
+
+ var count = math.max(hashset.Count, t1Set.Count);
+
+ var array = pool.Rent(count);
+ var collection = _pool.Take();
+ collection.Clear();
+
+ collection.UnionWith(hashset);
+ collection.IntersectWith(t1Set);
+
+ var i = 0;
+ foreach (var id in collection)
{
- if (entity.ServiceProvider.GetService() is { } t1 &&
- entity.ServiceProvider.GetService() is { } t2 &&
- entity.ServiceProvider.GetService() is { } t3 &&
- entity.ServiceProvider.GetService() is { } t4 &&
- entity.ServiceProvider.GetService() is { } t5 &&
- entity.ServiceProvider.GetService() is { } t6 &&
- entity.ServiceProvider.GetService() is { } t7 &&
- entity.ServiceProvider.GetService() is { } t8)
+ var instances = _typeInstances.GetOrCreate(id);
+
+ var h0 = typeof(T).GetHashCode();
+ var h1 = typeof(T1).GetHashCode();
+
+ if (instances.TryGetValue(h0, out var v0) is false)
{
- list.Add(new ValueTuple(t1, t2, t3, t4, t5, t6, t7, t8));
+ instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
}
+
+ if (instances.TryGetValue(h1, out var v1) is false)
+ {
+ instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ array[i] = (Unsafe.As(v0), Unsafe.As(v1));
+ i++;
+ }
+
+ try
+ {
+ return new Span<(T, T1)>(array, 0, i);
+ }
+ finally
+ {
+ pool.Return(array);
+ _pool.Return(collection);
+ }
+ }
+
+ public Span<(T, T1, T2)> QueryComponents() where T : class where T1 : class where T2 : class
+ {
+ var pool = ArrayPool<(T, T1, T2)>.Shared;
+
+ var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode());
+ var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode());
+ var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode());
+
+ var count = math.max(hashset.Count, math.max(t1Set.Count, t2Set.Count));
+
+ var array = pool.Rent(count);
+ var collection = _pool.Take();
+ collection.Clear();
+
+ collection.UnionWith(hashset);
+ collection.IntersectWith(t1Set);
+ collection.IntersectWith(t2Set);
+
+ var i = 0;
+ foreach (var id in collection)
+ {
+ var instances = _typeInstances.GetOrCreate(id);
+
+ var h0 = typeof(T).GetHashCode();
+ var h1 = typeof(T1).GetHashCode();
+ var h2 = typeof(T2).GetHashCode();
+
+ if (instances.TryGetValue(h0, out var v0) is false)
+ {
+ instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h1, out var v1) is false)
+ {
+ instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h2, out var v2) is false)
+ {
+ instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2));
+ i++;
+ }
+
+ try
+ {
+ return new Span<(T, T1, T2)>(array, 0, i);
+ }
+ finally
+ {
+ pool.Return(array);
+ _pool.Return(collection);
+ }
+ }
+
+ public Span<(T, T1, T2, T3)> QueryComponents()
+ where T : class where T1 : class where T2 : class where T3 : class
+ {
+ var pool = ArrayPool<(T, T1, T2, T3)>.Shared;
+
+ var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode());
+ var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode());
+ var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode());
+ var t3Set = _typeCaches.GetOrCreate(typeof(T3).GetHashCode());
+
+ var count = math.max(hashset.Count, math.max(t1Set.Count, math.max(t2Set.Count, t3Set.Count)));
+
+ var array = pool.Rent(count);
+ var collection = _pool.Take();
+ collection.Clear();
+
+ collection.UnionWith(hashset);
+ collection.IntersectWith(t1Set);
+ collection.IntersectWith(t2Set);
+ collection.IntersectWith(t3Set);
+
+ var i = 0;
+ foreach (var id in collection)
+ {
+ var instances = _typeInstances.GetOrCreate(id);
+
+ var h0 = typeof(T).GetHashCode();
+ var h1 = typeof(T1).GetHashCode();
+ var h2 = typeof(T2).GetHashCode();
+ var h3 = typeof(T3).GetHashCode();
+
+ if (instances.TryGetValue(h0, out var v0) is false)
+ {
+ instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h1, out var v1) is false)
+ {
+ instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h2, out var v2) is false)
+ {
+ instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h3, out var v3) is false)
+ {
+ instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2), Unsafe.As(v3));
+ i++;
+ }
+
+ try
+ {
+ return new Span<(T, T1, T2, T3)>(array, 0, i);
+ }
+ finally
+ {
+ pool.Return(array);
+ _pool.Return(collection);
+ }
+ }
+
+ public Span<(T, T1, T2, T3, T4)> QueryComponents() where T : class
+ where T1 : class
+ where T2 : class
+ where T3 : class
+ where T4 : class
+ {
+ var pool = ArrayPool<(T, T1, T2, T3, T4)>.Shared;
+
+ var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode());
+ var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode());
+ var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode());
+ var t3Set = _typeCaches.GetOrCreate(typeof(T3).GetHashCode());
+ var t4Set = _typeCaches.GetOrCreate(typeof(T4).GetHashCode());
+
+ var count = math.max(hashset.Count,
+ math.max(t1Set.Count, math.max(t2Set.Count, math.max(t3Set.Count, t4Set.Count))));
+
+ var array = pool.Rent(count);
+ var collection = _pool.Take();
+ collection.Clear();
+
+ collection.UnionWith(hashset);
+ collection.IntersectWith(t1Set);
+ collection.IntersectWith(t2Set);
+ collection.IntersectWith(t3Set);
+ collection.IntersectWith(t4Set);
+
+ var i = 0;
+ foreach (var id in collection)
+ {
+ var instances = _typeInstances.GetOrCreate(id);
+
+ var h0 = typeof(T).GetHashCode();
+ var h1 = typeof(T1).GetHashCode();
+ var h2 = typeof(T2).GetHashCode();
+ var h3 = typeof(T3).GetHashCode();
+ var h4 = typeof(T4).GetHashCode();
+
+ if (instances.TryGetValue(h0, out var v0) is false)
+ {
+ instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h1, out var v1) is false)
+ {
+ instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h2, out var v2) is false)
+ {
+ instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h3, out var v3) is false)
+ {
+ instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h4, out var v4) is false)
+ {
+ instances[h4] = v4 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2), Unsafe.As(v3),
+ Unsafe.As(v4));
+ i++;
+ }
+
+ try
+ {
+ return new Span<(T, T1, T2, T3, T4)>(array, 0, i);
+ }
+ finally
+ {
+ pool.Return(array);
+ _pool.Return(collection);
+ }
+ }
+
+ public Span<(T, T1, T2, T3, T4, T5)> QueryComponents() where T : class
+ where T1 : class
+ where T2 : class
+ where T3 : class
+ where T4 : class
+ where T5 : class
+ {
+ var pool = ArrayPool<(T, T1, T2, T3, T4, T5)>.Shared;
+
+ var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode());
+ var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode());
+ var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode());
+ var t3Set = _typeCaches.GetOrCreate(typeof(T3).GetHashCode());
+ var t4Set = _typeCaches.GetOrCreate(typeof(T4).GetHashCode());
+ var t5Set = _typeCaches.GetOrCreate(typeof(T5).GetHashCode());
+
+ var count = math.max(hashset.Count,
+ math.max(t1Set.Count,
+ math.max(t2Set.Count, math.max(t3Set.Count, math.max(t4Set.Count, t5Set.Count)))));
+
+ var array = pool.Rent(count);
+ var collection = _pool.Take();
+ collection.Clear();
+
+ collection.UnionWith(hashset);
+ collection.IntersectWith(t1Set);
+ collection.IntersectWith(t2Set);
+ collection.IntersectWith(t3Set);
+ collection.IntersectWith(t4Set);
+ collection.IntersectWith(t5Set);
+
+ var i = 0;
+ foreach (var id in collection)
+ {
+ var instances = _typeInstances.GetOrCreate(id);
+
+ var h0 = typeof(T).GetHashCode();
+ var h1 = typeof(T1).GetHashCode();
+ var h2 = typeof(T2).GetHashCode();
+ var h3 = typeof(T3).GetHashCode();
+ var h4 = typeof(T4).GetHashCode();
+ var h5 = typeof(T5).GetHashCode();
+
+ if (instances.TryGetValue(h0, out var v0) is false)
+ {
+ instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h1, out var v1) is false)
+ {
+ instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h2, out var v2) is false)
+ {
+ instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h3, out var v3) is false)
+ {
+ instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h4, out var v4) is false)
+ {
+ instances[h4] = v4 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h5, out var v5) is false)
+ {
+ instances[h5] = v5 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2), Unsafe.As(v3),
+ Unsafe.As(v4), Unsafe.As(v5));
+ i++;
+ }
+
+ try
+ {
+ return new Span<(T, T1, T2, T3, T4, T5)>(array, 0, i);
+ }
+ finally
+ {
+ pool.Return(array);
+ _pool.Return(collection);
+ }
+ }
+
+ public Span<(T, T1, T2, T3, T4, T5, T6)> QueryComponents() where T : class
+ where T1 : class
+ where T2 : class
+ where T3 : class
+ where T4 : class
+ where T5 : class
+ where T6 : class
+ {
+ var pool = ArrayPool<(T, T1, T2, T3, T4, T5, T6)>.Shared;
+
+ var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode());
+ var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode());
+ var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode());
+ var t3Set = _typeCaches.GetOrCreate(typeof(T3).GetHashCode());
+ var t4Set = _typeCaches.GetOrCreate(typeof(T4).GetHashCode());
+ var t5Set = _typeCaches.GetOrCreate(typeof(T5).GetHashCode());
+ var t6Set = _typeCaches.GetOrCreate(typeof(T6).GetHashCode());
+
+ var count = math.max(hashset.Count,
+ math.max(t1Set.Count,
+ math.max(t2Set.Count,
+ math.max(t3Set.Count, math.max(t4Set.Count, math.max(t5Set.Count, t6Set.Count))))));
+
+ var array = pool.Rent(count);
+ var collection = _pool.Take();
+ collection.Clear();
+
+ collection.UnionWith(hashset);
+ collection.IntersectWith(t1Set);
+ collection.IntersectWith(t2Set);
+ collection.IntersectWith(t3Set);
+ collection.IntersectWith(t4Set);
+ collection.IntersectWith(t5Set);
+ collection.IntersectWith(t6Set);
+
+ var i = 0;
+ foreach (var id in collection)
+ {
+ var instances = _typeInstances.GetOrCreate(id);
+
+ var h0 = typeof(T).GetHashCode();
+ var h1 = typeof(T1).GetHashCode();
+ var h2 = typeof(T2).GetHashCode();
+ var h3 = typeof(T3).GetHashCode();
+ var h4 = typeof(T4).GetHashCode();
+ var h5 = typeof(T5).GetHashCode();
+ var h6 = typeof(T6).GetHashCode();
+
+ if (instances.TryGetValue(h0, out var v0) is false)
+ {
+ instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h1, out var v1) is false)
+ {
+ instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h2, out var v2) is false)
+ {
+ instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h3, out var v3) is false)
+ {
+ instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h4, out var v4) is false)
+ {
+ instances[h4] = v4 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h5, out var v5) is false)
+ {
+ instances[h5] = v5 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ if (instances.TryGetValue(h6, out var v6) is false)
+ {
+ instances[h6] = v6 = _entitiesInternal[id].ServiceProvider.GetRequiredService();
+ }
+
+ array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2), Unsafe.As(v3),
+ Unsafe.As(v4), Unsafe.As(v5), Unsafe.As(v6));
+ i++;
+ }
+
+ try
+ {
+ return new Span<(T, T1, T2, T3, T4, T5, T6)>(array, 0, i);
+ }
+ finally
+ {
+ pool.Return(array);
+ _pool.Return(collection);
}
- return list.ToArray();
}
public void Dispose()
@@ -286,14 +625,14 @@ namespace BITKit.Entities
_count--;
if (_count <= 0)
{
- Entities.Clear();
+ _entitiesInternal.Clear();
}
-
+
_logger.LogInformation($"已释放,还剩{_count}个实例");
-
+
_cancellationTokenSource?.Dispose();
-
- _ticker.Remove(OnTick);
+
+ _ticker?.Remove(OnTick);
}
}
}
diff --git a/Src/Core/ECS/EntityComponentSystem.cs b/Src/Core/ECS/EntityComponentSystem.cs
index 81c0344..6fe951b 100644
--- a/Src/Core/ECS/EntityComponentSystem.cs
+++ b/Src/Core/ECS/EntityComponentSystem.cs
@@ -18,6 +18,7 @@ namespace BITKit.Entities
IServiceProvider ServiceProvider { get; }
IServiceCollection ServiceCollection { get; }
void Inject(object obj);
+
}
///
/// 基本实体服务
@@ -66,19 +67,13 @@ namespace BITKit.Entities
/// 通过Id获取或添加Entity
///
IEntity GetOrAdd(int id,Func factory);
-
- ///
- /// 查询Entity,例如
- ///
- /// var rotationEntities=EntitiesService.Query<RotationComponent>
- IEntity[] Query();
-
+
///
/// 查询1个组件
///
///
///
- T[] QueryComponents();
+ Span QueryComponents() where T : class;
///
/// 查询2个组件
@@ -86,7 +81,7 @@ namespace BITKit.Entities
///
///
///
- ValueTuple[] QueryComponents();
+ Span> QueryComponents() where T : class where T1 : class;
///
/// 查询3个组件
@@ -95,7 +90,7 @@ namespace BITKit.Entities
///
///
///
- ValueTuple[] QueryComponents();
+ Span> QueryComponents() where T : class where T1 : class where T2 : class;
///
/// 查询4个组件
@@ -105,7 +100,7 @@ namespace BITKit.Entities
///
///
///
- ValueTuple[] QueryComponents();
+ Span> QueryComponents() where T : class where T1 : class where T2 : class where T3:class;
///
/// 查询5个组件
@@ -116,7 +111,7 @@ namespace BITKit.Entities
///
///
///
- ValueTuple[] QueryComponents();
+ Span> QueryComponents()where T : class where T1 : class where T2 : class where T3:class where T4:class;
///
/// 查询6个组件
///
@@ -127,7 +122,7 @@ namespace BITKit.Entities
///
///
///
- ValueTuple[] QueryComponents();
+ Span> QueryComponents()where T : class where T1 : class where T2 : class where T3:class where T4:class where T5 : class;
///
/// 查询7个组件
@@ -140,7 +135,7 @@ namespace BITKit.Entities
///
///
///
- ValueTuple[] QueryComponents();
+ Span> QueryComponents()where T : class where T1 : class where T2 : class where T3:class where T4:class where T5 : class where T6 :class;
///
/// 查询8个组件
@@ -154,6 +149,6 @@ namespace BITKit.Entities
///
/// 剩余实例
///
- ValueTuple[] QueryComponents() where TRest : struct;
+ //Span> QueryComponents() where TRest : struct;
}
}
\ No newline at end of file
diff --git a/Src/Core/ECS/UnityEntity.cs b/Src/Core/ECS/UnityEntity.cs
index e959b66..4eef9a3 100644
--- a/Src/Core/ECS/UnityEntity.cs
+++ b/Src/Core/ECS/UnityEntity.cs
@@ -13,6 +13,7 @@ namespace BITKit.Entities
[DefaultExecutionOrder(-1)]
public class UnityEntity : MonoBehaviour,IEntity
{
+ [SerializeField] private bool debug;
private IEntitiesService _entitiesService;
private IEntity _entity;
@@ -22,6 +23,11 @@ namespace BITKit.Entities
if (_entitiesService.Entities.ContainsKey(gameObject.GetInstanceID())) return;
+ if (debug)
+ {
+
+ }
+
var entity = new Entity()
{
Id = gameObject.GetInstanceID(),
@@ -38,7 +44,10 @@ namespace BITKit.Entities
foreach (var component in GetComponents())
{
+ if(!component)continue;
var type = component.GetType();
+
+ entity.ServiceCollection.AddSingleton(type, component);
foreach (var x in type.GetInterfaces())
{
diff --git a/Src/Core/Extensions/IEnumerable.cs b/Src/Core/Extensions/IEnumerable.cs
index 670fa6f..58cc287 100644
--- a/Src/Core/Extensions/IEnumerable.cs
+++ b/Src/Core/Extensions/IEnumerable.cs
@@ -122,44 +122,20 @@ namespace BITKit
value = default;
return default;
}
- [Obsolete("Use TryGetElementAt instead")]
- public static bool TryGet(this IEnumerable self, int index, out T value)=>TryGetElementAt(self, index, out value);
-
- public static bool TryInsert(this IDictionary self, TKey key, TValue value)
+ public static TValue GetOrCreate(this IDictionary self, TKey t) where TValue : new()
{
- lock (self)
+ if (self.TryGetValue(t, out var value))
{
- if (self.ContainsKey(key))
- {
- self[key] = value;
- }
- else
- {
- self.Add(key, value);
- }
- return true;
- }
- }
- public static void Insert(this IDictionary self, TKey key, TValue value)
- {
- TryInsert(self, key, value);
- }
- public static TValue GetOrCreate(this IDictionary self, TKey t)
- {
- lock (self)
- {
- if (self.TryGetValue(t, out TValue value))
- {
- }
- else
- {
- value = Activator.CreateInstance();
- self.Add(t, value);
- }
- return value;
}
+ else
+ {
+ value = new TValue();
+ self.TryAdd(t, value);
+ }
+ return value;
}
+
public static bool TryRemove(this IList self, T t)
{
if (self.Contains(t))
diff --git a/Src/Core/Extensions/Object.cs b/Src/Core/Extensions/Object.cs
index b385165..68bc0b4 100644
--- a/Src/Core/Extensions/Object.cs
+++ b/Src/Core/Extensions/Object.cs
@@ -1,9 +1,64 @@
using System;
+using System.Collections.Concurrent;
+using System.Runtime.CompilerServices;
+using Microsoft.Extensions.DependencyInjection;
+using Unity.Mathematics;
namespace BITKit
{
public static partial class Extensions
{
+ private static readonly ConcurrentDictionary> Services = new();
+ public static bool QueryComponents(this IServiceProvider self,out T t1) where T : class
+ {
+ var id = self.GetHashCode();
+ var typeId = typeof(T).GetHashCode();
+ if (Services.GetOrCreate(id).TryGetValue(typeId, out var value) is false)
+ {
+ value = self.GetService();
+ Services.GetOrCreate(id).TryAdd(typeId,value);
+ }
+ if (value is null)
+ {
+ t1 = null;
+ return false;
+ }
+ t1 = Unsafe.As(value);
+ return true;
+ }
+ public static bool QueryComponents (this IServiceProvider self,out T t1,out T1 t2) where T : class where T1 : class
+ {
+ t1 = null;
+ t2 = null;
+ if (QueryComponents(self,out t1) is false) return false;
+ if (QueryComponents(self,out t2) is false) return false;
+ return true;
+ }
+
+ public static bool QueryComponents (this IServiceProvider self,out T t1,out T1 t2,out T2 t3) where T : class where T1 : class where T2 : class
+ {
+ t1 = null;
+ t2 = null;
+ t3 = null;
+ if (QueryComponents(self,out t1) is false) return false;
+ if (QueryComponents(self,out t2) is false) return false;
+ if (QueryComponents(self,out t3) is false) return false;
+ return true;
+ }
+
+ public static bool QueryComponents (this IServiceProvider self,out T t1,out T1 t2,out T2 t3,out T3 t4) where T : class where T1 : class where T2 : class where T3 : class
+ {
+ t1 = null;
+ t2 = null;
+ t3 = null;
+ t4 = null;
+ if (QueryComponents(self,out t1) is false) return false;
+ if (QueryComponents(self,out t2) is false) return false;
+ if (QueryComponents(self,out t3) is false) return false;
+ if (QueryComponents(self,out t4) is false) return false;
+ return true;
+ }
+
public static T IsNull(this T t, Action action)
{
if (t is null)
diff --git a/Src/Core/Group/ActivableGroup.cs b/Src/Core/Group/ActivableGroup.cs
deleted file mode 100644
index e01105b..0000000
--- a/Src/Core/Group/ActivableGroup.cs
+++ /dev/null
@@ -1,139 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq.Expressions;
-using Cysharp.Threading.Tasks;
-// ReSharper disable MethodHasAsyncOverload
-
-namespace BITKit
-{
- public interface IEntryGroup
- {
-
- }
- public interface IEntryElement
- {
- bool IsEntered { get; set; }
- void Entry();
- UniTask EntryAsync();
- void Entered();
- void Exit();
- UniTask ExitAsync();
- void Exited();
- }
-
- [System.Serializable]
- public class EntryGroup : IEntryGroup where T : IEntryElement
- {
- public int index = -1;
- public List list = new();
- private int m_index = -1;
- private bool completed=true;
-
- public event Action OnEntry;
- public event Action OnExit;
- public void Entry(T t)
- {
- if (t is not null)
- {
- list.TryAdd(t);
-
- }
- index = list.IndexOf(t);
- EnsureConfiguration();
- }
- public void Entry(int index)
- {
- if (index < list.Count)
- {
- this.index = index;
- }
- }
- public void Entry(Func entryFactory)
- {
- var index = list.FindIndex(x => entryFactory.Invoke(x));
- Entry(index);
- }
- public void Entry()
- {
- index = 0;
- EnsureConfiguration();
- }
-
- public void Exit()
- {
- index = -1;
- EnsureConfiguration();
- }
- public bool TryGetEntried(out T value)
- {
- EnsureConfiguration();
- if (m_index is not -1)
- {
- value = list[m_index];
- return true;
- }
- value = default;
- return false;
- }
- private async void EnsureConfiguration()
- {
- try
- {
- if(completed is false) return;
- completed = false;
- if (index == m_index)
- {
-
- }
- else
- {
- var currentIndex = m_index;
- if (currentIndex is not -1 && list.TryGetElementAt(currentIndex, out var currentElement))
- {
- currentElement.Exit();
- try
- {
- await currentElement.ExitAsync();
- }
- catch (OperationCanceledException)
- {
- return;
- }
- catch (Exception e)
- {
- BIT4Log.LogException(e);
- }
- currentElement.Exited();
- currentElement.IsEntered = false;
- OnExit?.Invoke(currentElement);
- }
- m_index = index;
- if (index is not -1 && list.TryGetElementAt(index, out var nextElement))
- {
- nextElement.IsEntered = true;
- OnEntry?.Invoke(nextElement);
- nextElement.Entry();
- try
- {
- await nextElement.EntryAsync();
- nextElement.Entered();
- }
- catch (OperationCanceledException){}
-
- }
- }
- completed = true;
- }
- catch (Exception e)
- {
- #if UNITY_EDITOR
- UnityEngine.Debug.LogException(e);
- #else
- BIT4Log.LogException(e);
- #endif
-
- }
-
- }
- }
-}
\ No newline at end of file
diff --git a/Src/Unity/Scripts/Quadtree/Items.meta b/Src/Core/Localization.meta
similarity index 77%
rename from Src/Unity/Scripts/Quadtree/Items.meta
rename to Src/Core/Localization.meta
index c0a6232..9fc096d 100644
--- a/Src/Unity/Scripts/Quadtree/Items.meta
+++ b/Src/Core/Localization.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: f97e02abd61aa274683a80be45fec110
+guid: c9f4b5601bd64124a9efdee9cf9c7226
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Src/Core/Localization/CsvLocalizationService.cs b/Src/Core/Localization/CsvLocalizationService.cs
new file mode 100644
index 0000000..70270d5
--- /dev/null
+++ b/Src/Core/Localization/CsvLocalizationService.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using BITKit;
+using Cysharp.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Net.BITKit.Localization
+{
+ public class CsvLocalizationService
+ {
+ public string Url { get; set; } =
+ @"http://server.bitfall.icu:21982/com.project.b/net.project.b.localization.csv";
+
+ private readonly ILogger _logger;
+ private readonly ILocalizationService _localizationService;
+
+ public CsvLocalizationService(ILocalizationService localizationService, ILogger logger)
+ {
+ _localizationService = localizationService;
+ _logger = logger;
+
+ _localizationService.OnLanguageChangeAsync += OnLanguageChangeAsync;
+ }
+
+ public static Dictionary> ParseCsvToDictionary(string csvData)
+ {
+ var dict = new Dictionary>();
+ using var reader = new StringReader(csvData);
+
+ var headerLine = reader.ReadLine();
+ if (headerLine == null) return dict;
+
+ var headers = headerLine.Split(',');
+
+ for (var i = 1; i < headers.Length; i++) // 跳过 "Key" 列
+ {
+ var header = headers[i];
+ if (dict.ContainsKey(header) is false)
+ {
+ dict.Add(header, new Dictionary());
+ }
+ }
+
+ string? line;
+ while ((line = reader.ReadLine()) != null)
+ {
+ var columns = line.Split(',');
+ if (columns.Length < 2) continue;
+
+ var key = columns[0]; // 取 Key 值
+ for (var i = 1; i < columns.Length; i++)
+ {
+ if(i>headers.Length-1)continue;
+ var header = headers[i];
+ if (dict.TryGetValue(header, out var d) is false)
+ {
+ d = new Dictionary();
+ dict.Add(header,d);
+ }
+ d.Set(key,columns[i]);
+ //dict[headers[i]][key] = columns[i]; // 填充语言数据
+ }
+ }
+
+ return dict;
+ }
+
+ private async UniTask OnLanguageChangeAsync(string arg1, string arg2)
+ {
+ var csv = await new HttpClient().GetStringAsync(Url+$"?{DateTime.Now.Ticks}");
+
+ _logger.LogInformation($"下载完成:\n{csv}");
+
+ foreach (var (lang, dictionary) in ParseCsvToDictionary(csv))
+ {
+ if (_localizationService.LocalizedStrings.TryGetValue(lang, out var currentDictionary) is false)
+ {
+ currentDictionary = new Dictionary();
+ _localizationService.LocalizedStrings.Add(lang,currentDictionary);
+ }
+ foreach (var (key, value) in dictionary)
+ {
+ currentDictionary.Set(key, value);
+ }
+ }
+ }
+ }
+
+}
diff --git a/Src/Unity/Scripts/Quadtree/GameObjectQuadtreeRoot.cs.meta b/Src/Core/Localization/CsvLocalizationService.cs.meta
similarity index 83%
rename from Src/Unity/Scripts/Quadtree/GameObjectQuadtreeRoot.cs.meta
rename to Src/Core/Localization/CsvLocalizationService.cs.meta
index 7017687..f836012 100644
--- a/Src/Unity/Scripts/Quadtree/GameObjectQuadtreeRoot.cs.meta
+++ b/Src/Core/Localization/CsvLocalizationService.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 2bc2bf5dce5a1fa418a53e9616109677
+guid: 0a0edc8f5d75b3646a7b405ef830a52d
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/Src/Core/Localization/ILocalizationService.cs b/Src/Core/Localization/ILocalizationService.cs
new file mode 100644
index 0000000..3a2355e
--- /dev/null
+++ b/Src/Core/Localization/ILocalizationService.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using BITKit;
+using Cysharp.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Net.BITKit.Localization
+{
+ ///
+ /// 本地化
+ ///
+ public interface ILocalizationService
+ {
+ public string Prefix { get; }
+ ///
+ /// 当前语言
+ ///
+ public string CurrentLanguage { get; }
+ ///
+ /// 更改回调,通常在此检查并下载语言包
+ ///
+ public event Func OnLanguageChangeAsync;
+ ///
+ /// 语言更改完成回调,UI和World执行更新
+ ///
+ public event Action OnLanguageChanged;
+ ///
+ /// 获取翻译文本,返回无复制的引用
+ ///
+ ///
+ /// 语言,默认为当前CultureInfo.Name
+ ///
+ public string GetLocalizedString(string key,string language=null);
+ public UniTask ChangeLanguageAsync(string newLanguage);
+ public IDictionary> LocalizedStrings { get; }
+ }
+
+ public class LocalizationService : ILocalizationService,IDisposable
+ {
+ private static LocalizationService _singleton;
+ public string Prefix => "#";
+ private char _prefix = '#';
+ public string CurrentLanguage { get; private set; }
+
+ public event Func OnLanguageChangeAsync;
+ public event Action OnLanguageChanged;
+
+ private readonly ILogger _logger;
+
+ public IDictionary> LocalizedStrings { get; } =
+ new Dictionary>();
+
+ private readonly ValidHandle _isBusy = new();
+
+ private readonly HashSet _untranslatedKeys = new();
+
+ public LocalizationService(ILogger logger)
+ {
+ if (_singleton is not null)
+ {
+ logger.LogError("LocalizationService can only be one singleton");
+ return;
+ }
+ _logger = logger;
+ }
+
+
+ public string GetLocalizedString(string key, string language = null)
+ {
+ language ??= CurrentLanguage; // 默认使用当前语言
+
+ if (key[0] != _prefix)
+ {
+ key = _prefix + key;
+ }
+ if (LocalizedStrings.TryGetValue(language, out var langDict) && langDict.TryGetValue(key, out var value))
+ {
+ return value;
+ }
+ _untranslatedKeys.Add(key);
+ return key.Replace(Prefix,string.Empty).Replace("_"," "); // 如果找不到翻译,就返回 key 本身(常见策略)
+ }
+ public async UniTask ChangeLanguageAsync(string newLanguage)
+ {
+ if (CurrentLanguage == newLanguage) return;
+ var oldLanguage = CurrentLanguage;
+ CurrentLanguage = newLanguage;
+
+ await _isBusy;
+ using var _ = _isBusy.GetHandle();
+
+ #if UNITY_5_3_OR_NEWER
+ await UniTask.SwitchToMainThread();
+#endif
+
+ await OnLanguageChangeAsync.UniTaskFunc(oldLanguage, newLanguage);
+
+ // 触发同步事件(例如更新 UI)
+ OnLanguageChanged?.Invoke(oldLanguage, newLanguage);
+ }
+
+ public void Dispose()
+ {
+ _isBusy?.Dispose();
+ _singleton = null;
+ _logger.LogInformation("Untranslated keys:\n"+string.Join("\n", _untranslatedKeys));
+ _untranslatedKeys.Clear();
+ }
+ }
+
+}
diff --git a/Src/Core/Group/ActivableGroup.cs.meta b/Src/Core/Localization/ILocalizationService.cs.meta
similarity index 83%
rename from Src/Core/Group/ActivableGroup.cs.meta
rename to Src/Core/Localization/ILocalizationService.cs.meta
index c8d6dd1..923b23f 100644
--- a/Src/Core/Group/ActivableGroup.cs.meta
+++ b/Src/Core/Localization/ILocalizationService.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 407c6cf54ae8abc42b77a090f9fa7616
+guid: baab5c8590ad6ee44acbc258eeacdc8b
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/Src/Core/Mod/ModService_IO.cs b/Src/Core/Mod/ModService_IO.cs
index c3b81cb..5d50316 100644
--- a/Src/Core/Mod/ModService_IO.cs
+++ b/Src/Core/Mod/ModService_IO.cs
@@ -19,6 +19,7 @@ namespace BITKit.Mod
{
list.Add((T)obj);
}
+
}
return list;
}
diff --git a/Src/Core/Quadtree.meta b/Src/Core/Quadtree.meta
new file mode 100644
index 0000000..3d76858
--- /dev/null
+++ b/Src/Core/Quadtree.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c15a9900918e4324991bcf53015b006e
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/Core/Quadtree/Net.BITKit.Quadtree.asmdef b/Src/Core/Quadtree/Net.BITKit.Quadtree.asmdef
new file mode 100644
index 0000000..0e53721
--- /dev/null
+++ b/Src/Core/Quadtree/Net.BITKit.Quadtree.asmdef
@@ -0,0 +1,20 @@
+{
+ "name": "Net.BITKit.Quadtree",
+ "rootNamespace": "",
+ "references": [
+ "GUID:14fe60d984bf9f84eac55c6ea033a8f4",
+ "GUID:d8b63aba1907145bea998dd612889d6b",
+ "GUID:f51ebe6a0ceec4240a699833d6309b23",
+ "GUID:e0cd26848372d4e5c891c569017e11f1",
+ "GUID:2665a8d13d1b3f18800f46e256720795"
+ ],
+ "includePlatforms": [],
+ "excludePlatforms": [],
+ "allowUnsafeCode": true,
+ "overrideReferences": false,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": [],
+ "versionDefines": [],
+ "noEngineReferences": true
+}
\ No newline at end of file
diff --git a/Src/Unity/Scripts/Quadtree/BITKit.Quadtree.asmdef.meta b/Src/Core/Quadtree/Net.BITKit.Quadtree.asmdef.meta
similarity index 100%
rename from Src/Unity/Scripts/Quadtree/BITKit.Quadtree.asmdef.meta
rename to Src/Core/Quadtree/Net.BITKit.Quadtree.asmdef.meta
diff --git a/Src/Core/Quadtree/Quadtree.cs b/Src/Core/Quadtree/Quadtree.cs
new file mode 100644
index 0000000..82e32ba
--- /dev/null
+++ b/Src/Core/Quadtree/Quadtree.cs
@@ -0,0 +1,305 @@
+using System;
+using System.Buffers;
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http.Headers;
+using System.Security.Policy;
+using System.Threading;
+using System.Threading.Tasks;
+using BITKit;
+#if UNITY_5_3_OR_NEWER
+using Unity.Burst;
+#endif
+using Unity.Mathematics;
+
+namespace Net.BITKit.Quadtree
+{
+ public class Quadtree
+ {
+ public QuadtreeNode Root { get; private set; }
+
+ public IDictionary Positions
+ {
+ get
+ {
+ Expansion();
+ return _positions;
+ }
+ }
+
+ public IDictionary Sizes => _sizes;
+ private readonly Dictionary _positions;
+ private readonly ConcurrentQueue<(int, float2)> _queue;
+ private readonly Dictionary _sizes;
+
+ public Quadtree(float2 center, float2 size)
+ {
+ _sizes = new Dictionary();
+ _positions = new Dictionary();
+ _queue = new ConcurrentQueue<(int, float2)>();
+ Root = new QuadtreeNode(this, center, size);
+ }
+
+ public void Insert(in int objectId, in float2 position, in float2 size = default)
+ {
+ _queue.Enqueue((objectId, position));
+
+ if (size.x is not 0)
+ {
+ _sizes.TryAdd(objectId, size);
+ }
+ else
+ {
+ var root = Root;
+
+ InsertRecursive(ref root, in objectId, in position);
+ }
+ }
+
+ private void InsertRecursive(ref QuadtreeNode node, in int objectId, in float2 position)
+ {
+ if (!node.Contains(position))
+ return;
+ if (node.Objects.Count < 4 || node.Size.x <= 1f || node.Depth > Root.Size.x / 8) // 假设最小节点大小为1
+ {
+ node.Objects.Add(objectId);
+ }
+ else
+ {
+ if (node.Children[0].Size.x == 0) // 如果子节点未初始化
+ {
+ node.Split();
+ }
+
+ for (var i = 0; i < 4; i++)
+ {
+ InsertRecursive(ref node.Children[i], objectId, position);
+ }
+ }
+ }
+
+ public Span Query(float2 position, float radius)
+ {
+ Expansion();
+
+ var index = 0;
+
+ var root = Root;
+
+ var pool = ArrayPool.Shared;
+
+ var array = pool.Rent(math.ceilpow2(_positions.Count * 2));
+
+ pool.Return(array);
+
+ QueryRecursive(in root, in position, in radius, array.AsSpan(), ref index);
+ foreach (var (key, size) in _sizes)
+ {
+ var pos = _positions[key];
+
+ if (IsCircleInRect(pos, radius, position, size))
+ {
+ array[index] = key;
+ index++;
+ }
+ }
+
+ try
+ {
+ return array.AsSpan()[..index];
+ }
+ finally
+ {
+ pool.Return(array);
+ }
+ }
+#if UNITY_5_3_OR_NEWER
+ [BurstCompile]
+#endif
+ private static bool IsCircleInRect(float2 point, float radius, float2 pos, float2 size)
+ {
+ var halfSize = size * 0.5f;
+ var min = pos - halfSize; // 矩形左下角
+ var max = pos + halfSize; // 矩形右上角
+
+ // 计算扩展后的包围盒
+ var expandedMin = min - radius;
+ var expandedMax = max + radius;
+
+ // 检查点是否在扩展的矩形内
+ return point.x >= expandedMin.x && point.x <= expandedMax.x &&
+ point.y >= expandedMin.y && point.y <= expandedMax.y;
+ }
+
+ private void Expansion()
+ {
+ while (_queue.TryDequeue(out var item))
+ {
+ _positions.TryAdd(item.Item1, item.Item2);
+ }
+ }
+#if UNITY_5_3_OR_NEWER
+ [BurstCompile]
+#endif
+ private void QueryRecursive(in QuadtreeNode node, in float2 position, in float radius, Span result,
+ ref int index)
+ {
+ if (!Intersects(node.Center, node.Size, position, radius))
+ return;
+
+
+
+ foreach (var obj in node.Objects)
+ {
+ // 直接 TryGetValue,避免 `Positions[obj]` 可能的重复哈希查找
+ if (!_positions.TryGetValue(obj, out var objPos)) continue;
+
+ // 计算平方距离,避免额外变量
+ if (math.dot(objPos - position, objPos - position) > radius * radius) continue;
+
+ // 直接写入 result
+ try
+ {
+ result[index] = obj;
+ }
+ catch (Exception)
+ {
+ #if UNITY_EDITOR
+ var unions = result.ToArray().Distinct();
+ var hashSet = new HashSet();
+ For(Root);
+ void For(in QuadtreeNode node)
+ {
+ foreach (var child in node.Children)
+ {
+ For(child);
+ }
+ foreach (var x in node.Objects)
+ {
+ if (hashSet.Add(x) is false)
+ {
+
+ }
+ }
+ }
+ #endif
+ throw;
+ }
+
+ index++;
+ }
+
+
+ for (var i = 0; i < 4; i++)
+ {
+ if (node.Children[i].Size.x > 0)
+ {
+ QueryRecursive(in node.Children[i], in position, in radius, result, ref index);
+ }
+ }
+ }
+#if UNITY_5_3_OR_NEWER
+ [BurstCompile]
+#endif
+ private static bool Intersects(in float2 center, in float2 size, in float2 position, in float radius)
+ {
+ // 计算 AABB 的最小/最大点
+ var min = center - size * 0.5f;
+ var max = center + size * 0.5f;
+
+ // 找到圆心到 AABB 的最近点
+ var closest = math.clamp(position, min, max);
+
+ // 计算圆心到最近点的距离
+ var delta = position - closest;
+ var distanceSq = math.dot(delta, delta);
+
+ // 相交条件:如果距离平方 <= 半径平方
+ return distanceSq <= (radius * radius);
+ }
+
+
+ // 删除对象
+ public bool Remove(int objectId)
+ {
+ Expansion();
+
+ if (_positions.TryGetValue(objectId, out var position) is false) return false;
+
+ _sizes.TryRemove(objectId);
+
+ var root = Root;
+
+ if (RemoveRecursive(ref root, objectId, position) is false) return false;
+ _positions.Remove(objectId);
+
+
+ return true;
+ }
+ private static bool RemoveRecursive(ref QuadtreeNode node, int objectId, float2 position)
+ {
+ if (!node.Contains(position))
+ return false;
+
+ // 尝试从当前节点删除
+ bool removed = node.Objects.Remove(objectId);
+
+ // 如果当前节点是叶子节点,返回是否成功删除
+ if (node.IsLeaf())
+ {
+ return removed;
+ }
+
+ // 递归从子节点删除
+ for (var i = 0; i < 4; i++)
+ {
+ if (RemoveRecursive(ref node.Children[i], objectId, position))
+ {
+ removed = true;
+ }
+ }
+
+ // 如果对象被移除,尝试合并子节点
+ if (removed)
+ {
+ TryMerge(ref node);
+ }
+
+ return removed;
+ }
+
+ private static void TryMerge(ref QuadtreeNode node)
+ {
+ if (node.IsLeaf()) return;
+
+ // 检查是否可以合并子节点
+ var totalObjects = node.Objects.Count;
+ for (var i = 0; i < 4; i++)
+ {
+ totalObjects += node.Children[i].Objects.Count;
+ }
+
+ // 如果当前节点和所有子节点的对象数量小于等于阈值,则合并
+ if (totalObjects <= 4)
+ {
+ // 把所有子节点的对象都合并到当前节点
+ foreach (var child in node.Children)
+ {
+ node.Objects.UnionWith(child.Objects);
+ child.Objects.Clear(); // 清空子节点的对象集合
+ }
+
+ // 清空子节点,重新初始化
+ for (var i = 0; i < 4; i++)
+ {
+ node.Children[i] = new QuadtreeNode()
+ {
+ Depth = node.Depth + 1
+ };
+ }
+ }
+ }
+ }
+}
diff --git a/Src/Unity/Scripts/Quadtree/INode.cs.meta b/Src/Core/Quadtree/Quadtree.cs.meta
similarity index 83%
rename from Src/Unity/Scripts/Quadtree/INode.cs.meta
rename to Src/Core/Quadtree/Quadtree.cs.meta
index ad87d95..3ad1d45 100644
--- a/Src/Unity/Scripts/Quadtree/INode.cs.meta
+++ b/Src/Core/Quadtree/Quadtree.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 097957ac1375f8141a80aa94f32030db
+guid: b421b3302ecaaab4bab9eebf970e2bb6
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/Src/Core/Quadtree/QuadtreeNode.cs b/Src/Core/Quadtree/QuadtreeNode.cs
new file mode 100644
index 0000000..ef7d1e9
--- /dev/null
+++ b/Src/Core/Quadtree/QuadtreeNode.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Buffers;
+using System.Collections.Concurrent;
+using Unity.Mathematics;
+using System.Collections.Generic;
+
+namespace Net.BITKit.Quadtree
+{
+ public struct QuadtreeNode
+ {
+ public readonly Quadtree Root;
+
+ public int Depth;
+
+ public float2 Center; // 节点中心
+ public float2 Size; // 节点大小
+
+ public readonly QuadtreeNode[] Children; // 子节点
+ public readonly HashSet Objects; // 存储的对象ID
+
+ public QuadtreeNode(Quadtree root,float2 center, float2 size)
+ {
+ Root = root;
+ Center = center;
+ Size = size;
+
+ Children = new QuadtreeNode[4];
+
+ Objects = new HashSet();
+
+ Depth = 0;
+ }
+
+ public bool Contains(float2 point)
+ {
+ var min = Center - Size * 0.5f;
+ var max = Center + Size * 0.5f;
+
+ return math.all(point >= min & point <= max);
+ }
+
+ public void Split()
+ {
+ var childSize = Size / 2;
+ var offset = childSize / 2;
+
+ foreach (var node in Children)
+ {
+ foreach (var id in Objects)
+ {
+ var pos = Root.Positions[id];
+ if (node.Contains(pos))
+ {
+ node.Objects.Add(id);
+ }
+ }
+ }
+
+ Objects.Clear();
+
+ Children[0] = new QuadtreeNode(Root, Center + new float2(-offset.x, offset.y), childSize)
+ {
+ Depth = Depth + 1,
+ };
+ Children[1] = new QuadtreeNode(Root, Center + new float2(offset.x, offset.y), childSize)
+ {
+ Depth = Depth + 1,
+ };
+ Children[2] = new QuadtreeNode(Root, Center + new float2(-offset.x, -offset.y), childSize)
+ {
+ Depth = Depth + 1,
+ };
+ Children[3] = new QuadtreeNode(Root, Center + new float2(offset.x, -offset.y), childSize)
+ {
+ Depth = Depth + 1,
+ };
+ }
+
+ public bool IsLeaf()
+ {
+ return Children[0].Size.x == 0; // 如果子节点未初始化,说明是叶子节点
+ }
+ }
+}
diff --git a/Src/Unity/Scripts/Quadtree/IQuadtreeRoot.cs.meta b/Src/Core/Quadtree/QuadtreeNode.cs.meta
similarity index 83%
rename from Src/Unity/Scripts/Quadtree/IQuadtreeRoot.cs.meta
rename to Src/Core/Quadtree/QuadtreeNode.cs.meta
index a1cc8f6..f38829c 100644
--- a/Src/Unity/Scripts/Quadtree/IQuadtreeRoot.cs.meta
+++ b/Src/Core/Quadtree/QuadtreeNode.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 625588063be34ab419d783b87c474a44
+guid: 837e58f0e4d63c2438b1037a76a5a6ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/Src/Core/Sharp/BITSharp.cs b/Src/Core/Sharp/BITSharp.cs
index 46cef9f..f670868 100644
--- a/Src/Core/Sharp/BITSharp.cs
+++ b/Src/Core/Sharp/BITSharp.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BITKit.Mod;
@@ -37,6 +38,7 @@ namespace BITKit
}
public class CodeGenerator:ICodeGenerator
{
+
public virtual string Generate(Type type, string className = null)
{
if (type.IsInterface is false) throw new InvalidDataException("The type have to be interface");
diff --git a/Src/Core/Tween/BITween.cs b/Src/Core/Tween/BITween.cs
index ce9c997..c2bcfda 100644
--- a/Src/Core/Tween/BITween.cs
+++ b/Src/Core/Tween/BITween.cs
@@ -35,7 +35,25 @@ namespace BITKit.Tween
return new TweenSequence();
}
- public static async UniTask Lerp(Action setter,T from,T to,float duration, Func lerp,CancellationToken cancellationToken = default)
+ public static async UniTask MoveToForward(Action setter, T from, T to, float delta,
+ Func func, CancellationToken cancellationToken = default)
+ {
+ setter(from);
+ while (Equals(from,to) is false && cancellationToken.IsCancellationRequested is false)
+ {
+ from = func(from, to, delta*BITApp.Time.DeltaTime);
+#if UNITY_5_3_OR_NEWER
+ await UniTask.NextFrame(cancellationToken);
+#else
+
+ await UniTask.Yield();
+#endif
+ setter(from);
+ }
+ if (cancellationToken.IsCancellationRequested is false)
+ setter(to);
+ }
+ public static async UniTask Lerp(Action setter,T from,T to,float duration, Func func,CancellationToken cancellationToken = default)
{
var t = 0f;
var delta = 1f / duration;
@@ -44,7 +62,7 @@ namespace BITKit.Tween
while (t < 1 && cancellationToken.IsCancellationRequested is false)
{
t = math.clamp(t + delta*BITApp.Time.DeltaTime, 0, 1);
- var next = lerp(from, to, t);
+ var next = func(from, to, t);
#if UNITY_5_3_OR_NEWER
await UniTask.NextFrame(cancellationToken);
#else
diff --git a/Src/Core/UX/IUXDialogue.cs b/Src/Core/UX/IUXDialogue.cs
index 1323284..990f9c7 100644
--- a/Src/Core/UX/IUXDialogue.cs
+++ b/Src/Core/UX/IUXDialogue.cs
@@ -7,7 +7,6 @@ namespace BITKit.UX
public interface IUXDialogue
{
void Show(string content,string title = "Alert",Action confirmAction=null,Action onChoose=null);
-
-
+
}
}
diff --git a/Src/Core/UX/IUXPanel.cs b/Src/Core/UX/IUXPanel.cs
index 07eec72..97b6292 100644
--- a/Src/Core/UX/IUXPanel.cs
+++ b/Src/Core/UX/IUXPanel.cs
@@ -1,4 +1,6 @@
using System;
+using System.Runtime.CompilerServices;
+using BITKit.StateMachine;
using Cysharp.Threading.Tasks;
// ReSharper disable UnassignedGetOnlyAutoProperty
@@ -12,7 +14,7 @@ namespace BITKit.UX
/// ⭐异步打开与关闭
/// ⭐当前可见状态
/// ⭐基本UI导航回调
- public interface IUXPanel:IEntryElement
+ public interface IUXPanel:IStateAsync
{
///
/// 是否为窗口,用于覆盖其他面板
diff --git a/Src/Core/Utils/Data.cs b/Src/Core/Utils/Data.cs
index 865efb5..50f005a 100644
--- a/Src/Core/Utils/Data.cs
+++ b/Src/Core/Utils/Data.cs
@@ -79,9 +79,6 @@ namespace BITKit
var action = x as Action;
action.Invoke(value);
});
- if (value is not JToken)
- Objects.TryInsert(key, value);
-
}
public static bool TryGetValue(string key, out T value)
{
diff --git a/Src/Core/Wrapper/IWrapper.cs b/Src/Core/Wrapper/IWrapper.cs
index e782676..86cc7de 100644
--- a/Src/Core/Wrapper/IWrapper.cs
+++ b/Src/Core/Wrapper/IWrapper.cs
@@ -19,7 +19,10 @@ namespace BITKit
var type = typeof(T);
if (type == typeof(string))
{
- _value = string.Empty.As();
+ if (string.Empty is T t)
+ {
+ _value = t;
+ }
return;
}
if(type.IsAbstract || type.IsInterface)return;
diff --git a/Src/Core/kcp2k/kcp/Pool.cs b/Src/Core/kcp2k/kcp/Pool.cs
index 81b5289..717a0ef 100644
--- a/Src/Core/kcp2k/kcp/Pool.cs
+++ b/Src/Core/kcp2k/kcp/Pool.cs
@@ -33,7 +33,7 @@ namespace kcp2k
// return an element to the pool
public void Return(T item)
{
- objectResetter(item);
+ objectResetter?.Invoke(item);
objects.Push(item);
}
diff --git a/Src/Unity/BitMask.meta b/Src/Unity/BitMask.meta
new file mode 100644
index 0000000..667aa9f
--- /dev/null
+++ b/Src/Unity/BitMask.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c72b222167e45bb4b91034232c2164b5
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/Unity/BitMask/Net.Project.B.BitMask.Unity.asmdef b/Src/Unity/BitMask/Net.Project.B.BitMask.Unity.asmdef
new file mode 100644
index 0000000..724e3fd
--- /dev/null
+++ b/Src/Unity/BitMask/Net.Project.B.BitMask.Unity.asmdef
@@ -0,0 +1,19 @@
+{
+ "name": "Net.Project.B.BitMask.Unity",
+ "rootNamespace": "",
+ "references": [
+ "GUID:14fe60d984bf9f84eac55c6ea033a8f4",
+ "GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
+ "GUID:d525ad6bd40672747bde77962f1c401e",
+ "GUID:49b49c76ee64f6b41bf28ef951cb0e50"
+ ],
+ "includePlatforms": [],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": false,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": [],
+ "versionDefines": [],
+ "noEngineReferences": false
+}
\ No newline at end of file
diff --git a/Src/UnityPluginsSupport/Polaris/BITKit.Extensions.Polaris.asmdef.meta b/Src/Unity/BitMask/Net.Project.B.BitMask.Unity.asmdef.meta
similarity index 76%
rename from Src/UnityPluginsSupport/Polaris/BITKit.Extensions.Polaris.asmdef.meta
rename to Src/Unity/BitMask/Net.Project.B.BitMask.Unity.asmdef.meta
index a8f6fb9..8f499bd 100644
--- a/Src/UnityPluginsSupport/Polaris/BITKit.Extensions.Polaris.asmdef.meta
+++ b/Src/Unity/BitMask/Net.Project.B.BitMask.Unity.asmdef.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 4f23b1d01b5fa044784cf0a3dd9794eb
+guid: 1f7ecae3bbd1640418dd25dd62e6a9d3
AssemblyDefinitionImporter:
externalObjects: {}
userData:
diff --git a/Src/Unity/BitMask/NewScriptableBitMask.asset b/Src/Unity/BitMask/NewScriptableBitMask.asset
new file mode 100644
index 0000000..eedb884
--- /dev/null
+++ b/Src/Unity/BitMask/NewScriptableBitMask.asset
@@ -0,0 +1,17 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4bc860277386c244aac5bc8ec6cbb54b, type: 3}
+ m_Name: NewScriptableBitMask
+ m_EditorClassIdentifier:
+ implements: {fileID: 11400000, guid: 1e642bf42ffa8bd429cbfdeeefabaecd, type: 2}
+ dictionary:
+ _serializedList: []
diff --git a/Src/Unity/BitMask/NewScriptableBitMask.asset.meta b/Src/Unity/BitMask/NewScriptableBitMask.asset.meta
new file mode 100644
index 0000000..168183d
--- /dev/null
+++ b/Src/Unity/BitMask/NewScriptableBitMask.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8411467f1adb3754ba46f890d7be5ff4
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/Unity/BitMask/NewScriptableBitMaskSample.asset b/Src/Unity/BitMask/NewScriptableBitMaskSample.asset
new file mode 100644
index 0000000..b510c21
--- /dev/null
+++ b/Src/Unity/BitMask/NewScriptableBitMaskSample.asset
@@ -0,0 +1,38 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 9fab0368c5976f24dafd43f829e91aab, type: 3}
+ m_Name: NewScriptableBitMaskSample
+ m_EditorClassIdentifier:
+ implements:
+ - rid: 7572707602352832517
+ - rid: 7572707602352832518
+ - rid: 7572707602352832519
+ - rid: 7572707602352832520
+ - rid: 7572707602352832521
+ references:
+ version: 2
+ RefIds:
+ - rid: 7572707602352832517
+ type: {class: SampleInterface1, ns: Net.BITKit.BitMask, asm: Net.Project.B.BitMask.Unity}
+ data:
+ - rid: 7572707602352832518
+ type: {class: SampleInterface2, ns: Net.BITKit.BitMask, asm: Net.Project.B.BitMask.Unity}
+ data:
+ - rid: 7572707602352832519
+ type: {class: SampleInterface3, ns: Net.BITKit.BitMask, asm: Net.Project.B.BitMask.Unity}
+ data:
+ - rid: 7572707602352832520
+ type: {class: SampleInterface4, ns: Net.BITKit.BitMask, asm: Net.Project.B.BitMask.Unity}
+ data:
+ - rid: 7572707602352832521
+ type: {class: SampleInterface5, ns: Net.BITKit.BitMask, asm: Net.Project.B.BitMask.Unity}
+ data:
diff --git a/Src/Unity/BitMask/NewScriptableBitMaskSample.asset.meta b/Src/Unity/BitMask/NewScriptableBitMaskSample.asset.meta
new file mode 100644
index 0000000..2aaea86
--- /dev/null
+++ b/Src/Unity/BitMask/NewScriptableBitMaskSample.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1e642bf42ffa8bd429cbfdeeefabaecd
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Src/Unity/BitMask/ScriptableBitMask.cs b/Src/Unity/BitMask/ScriptableBitMask.cs
new file mode 100644
index 0000000..e73feb6
--- /dev/null
+++ b/Src/Unity/BitMask/ScriptableBitMask.cs
@@ -0,0 +1,192 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using AYellowpaper.SerializedCollections;
+using BITKit;
+using NUnit.Framework;
+using UnityEngine;
+using UnityEngine.Pool;
+using UnityEngine.UIElements;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
+#if UNITY_EDITOR
+ using UnityEditor;
+#endif
+namespace Net.BITKit.BitMask
+{
+
+ public class ScriptableBitMask : ScriptableObject,ISerializationCallbackReceiver
+ {
+ [SerializeField] public ScriptableImplements implements;
+
+ internal readonly Dictionary> Dictionary=new();
+ [SerializeField] public string yaml;
+
+ public Type Type => implements.Type;
+
+ public Type[] Types => implements ? implements.Types : Array.Empty();
+
+ private void OnEnable()
+ {
+ OnBeforeSerialize();
+ }
+
+ public void OnBeforeSerialize()
+ {
+ if(string.IsNullOrEmpty(yaml))return;
+ var deserializer = new DeserializerBuilder()
+ .WithNamingConvention(CamelCaseNamingConvention.Instance) // 驼峰命名
+ .Build();
+ Dictionary.Clear();
+ var newDictionary = deserializer.Deserialize>>(yaml);
+ foreach (var pair in newDictionary)
+ {
+ Dictionary[pair.Key] = pair.Value;
+ }
+ }
+
+ public void OnAfterDeserialize()
+ {
+ if(Dictionary.Count is 0)return;
+ var serializer = new SerializerBuilder()
+ .WithNamingConvention(CamelCaseNamingConvention.Instance)
+ .Build();
+ yaml = serializer.Serialize(Dictionary);
+ }
+ public IDictionary> FlagMask
+ {
+ get
+ {
+ var typeDictionary = implements.Types.ToDictionary(x => x.Name, x => x);
+
+ var nextDictionary = new Dictionary>();
+
+ foreach (var (key, d) in Dictionary)
+ {
+ if (typeDictionary.TryGetValue(key, out var type) is false) continue;
+ var hashSet = new HashSet();
+ foreach (var x in d)
+ {
+ if(typeDictionary.TryGetValue(x,out var t1) is false)continue;
+ hashSet.Add(t1);
+ }
+ nextDictionary[type]=hashSet;
+ }
+
+ foreach (var type in Types)
+ {
+ if (nextDictionary.TryAdd(type, new HashSet