This commit is contained in:
CortexCore
2025-03-24 14:42:40 +08:00
parent 18239a5ae4
commit 9845d20f7f
99 changed files with 5418 additions and 5512 deletions

View File

@@ -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<EntitiesService> _logger;
private readonly IFixedTicker _ticker;
private static int _count;
private static readonly ConcurrentQueue<IEntity> OnAddQueue = new();
private static ConcurrentDictionary<int, HashSet<int>> TypeCaches = new();
public EntitiesService(ILogger<EntitiesService> logger, IFixedTicker ticker)
internal static T GetRequiredServiceWithId<T>(this IServiceProvider serviceProvider, int id)
{
if (_count > 0)
{
throw new MulticastNotSupportedException();
}
_count++;
return serviceProvider.GetRequiredService<T>();
}
}
public unsafe class EntitiesService : IEntitiesService, IDisposable
{
private static int _count;
private readonly ILogger<EntitiesService> _logger;
private readonly ITicker _ticker;
private readonly ConcurrentQueue<IEntity> _onAddQueue = new();
private readonly Dictionary<int, HashSet<int>> _typeCaches = new();
private readonly Dictionary<int, ConcurrentDictionary<int, object>> _typeInstances = new();
private readonly ConcurrentDictionary<int, object> _staticCaches = new();
public EntitiesService(ILogger<EntitiesService> 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<int, IEntity> 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<int, IEntity> _entitiesInternal = new();
public event Action<IEntity> OnAdd;
public event Action<IEntity> OnRemove;
IReadOnlyDictionary<int, IEntity> IEntitiesService.Entities => Entities;
IReadOnlyDictionary<int, IEntity> IEntitiesService.Entities => _entitiesInternal;
private readonly Pool<HashSet<int>> _pool;
private HashSet<int> ObjectGenerator()
{
return new HashSet<int>(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<int, IEntity> factory)
{
return Entities.GetOrAdd(id, factory);
}
public IEntity[] Query<T>()
{
throw new NotImplementedException("Obsoleted");
}
public T[] QueryComponents<T>()
{
var list = new List<T>();
foreach (var entity in Entities.Values)
if (_entitiesInternal.TryGetValue(id, out var current))
{
if (entity.ServiceProvider.GetService<T>() 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<T>().ToArray();
return current;
}
public (T, T1)[] QueryComponents<T, T1>()
public Span<T> QueryComponents<T>() where T : class
{
var list = new List<(T, T1)>();
foreach (var entity in Entities.Values)
{
if (entity.ServiceProvider.GetService<T>() is { } t1 &&
entity.ServiceProvider.GetService<T1>() 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<T, T1, T2>()
{
var list = new List<(T, T1, T2)>();
foreach (var entity in Entities.Values)
{
if (entity.ServiceProvider.GetService<T>() is { } t1 &&
entity.ServiceProvider.GetService<T1>() is { } t2 &&
entity.ServiceProvider.GetService<T2>() 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<T>.Shared;
public (T, T1, T2, T3)[] QueryComponents<T, T1, T2, T3>()
{
var list = new List<(T, T1, T2, T3)>();
foreach (var entity in Entities.Values)
{
if (entity.ServiceProvider.GetService<T>() is { } t1 &&
entity.ServiceProvider.GetService<T1>() is { } t2 &&
entity.ServiceProvider.GetService<T2>() is { } t3 &&
entity.ServiceProvider.GetService<T3>() 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<T, T1, T2, T3, T4>()
{
var list = new List<(T, T1, T2, T3, T4)>();
foreach (var entity in Entities.Values)
{
if (entity.ServiceProvider.GetService<T>() is { } t1 &&
entity.ServiceProvider.GetService<T1>() is { } t2 &&
entity.ServiceProvider.GetService<T2>() is { } t3 &&
entity.ServiceProvider.GetService<T3>() is { } t4 &&
entity.ServiceProvider.GetService<T4>() 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<T, T1, T2, T3, T4, T5>()
{
var list = new List<(T, T1, T2, T3, T4, T5)>();
foreach (var entity in Entities.Values)
{
if (entity.ServiceProvider.GetService<T>() is { } t1 &&
entity.ServiceProvider.GetService<T1>() is { } t2 &&
entity.ServiceProvider.GetService<T2>() is { } t3 &&
entity.ServiceProvider.GetService<T3>() is { } t4 &&
entity.ServiceProvider.GetService<T4>() is { } t5 &&
entity.ServiceProvider.GetService<T5>() 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<T, T1, T2, T3, T4, T5, T6>()
{
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<T>() is { } t1 &&
entity.ServiceProvider.GetService<T1>() is { } t2 &&
entity.ServiceProvider.GetService<T2>() is { } t3 &&
entity.ServiceProvider.GetService<T3>() is { } t4 &&
entity.ServiceProvider.GetService<T4>() is { } t5 &&
entity.ServiceProvider.GetService<T5>() is { } t6 &&
entity.ServiceProvider.GetService<T6>() 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<T>();
}
array[i] = Unsafe.As<T>(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<T>(array, 0, i);
}
finally
{
pool.Return(array);
_pool.Return(collection);
}
}
public ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>[] QueryComponents<T, T1, T2, T3, T4, T5, T6, TRest>()
where TRest : struct
public Span<(T, T1)> QueryComponents<T, T1>() where T : class where T1 : class
{
var list = new List<ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>>();
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<T>() is { } t1 &&
entity.ServiceProvider.GetService<T1>() is { } t2 &&
entity.ServiceProvider.GetService<T2>() is { } t3 &&
entity.ServiceProvider.GetService<T3>() is { } t4 &&
entity.ServiceProvider.GetService<T4>() is { } t5 &&
entity.ServiceProvider.GetService<T5>() is { } t6 &&
entity.ServiceProvider.GetService<T6>() is { } t7 &&
entity.ServiceProvider.GetService<TRest>() 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<T, T1, T2, T3, T4, T5, T6, TRest>(t1, t2, t3, t4, t5, t6, t7, t8));
instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T>();
}
if (instances.TryGetValue(h1, out var v1) is false)
{
instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T1>();
}
array[i] = (Unsafe.As<T>(v0), Unsafe.As<T1>(v1));
i++;
}
try
{
return new Span<(T, T1)>(array, 0, i);
}
finally
{
pool.Return(array);
_pool.Return(collection);
}
}
public Span<(T, T1, T2)> QueryComponents<T, T1, T2>() 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<T>();
}
if (instances.TryGetValue(h1, out var v1) is false)
{
instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T1>();
}
if (instances.TryGetValue(h2, out var v2) is false)
{
instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T2>();
}
array[i] = (Unsafe.As<T>(v0), Unsafe.As<T1>(v1), Unsafe.As<T2>(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<T, T1, T2, T3>()
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<T>();
}
if (instances.TryGetValue(h1, out var v1) is false)
{
instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T1>();
}
if (instances.TryGetValue(h2, out var v2) is false)
{
instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T2>();
}
if (instances.TryGetValue(h3, out var v3) is false)
{
instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T3>();
}
array[i] = (Unsafe.As<T>(v0), Unsafe.As<T1>(v1), Unsafe.As<T2>(v2), Unsafe.As<T3>(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<T, T1, T2, T3, T4>() 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<T>();
}
if (instances.TryGetValue(h1, out var v1) is false)
{
instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T1>();
}
if (instances.TryGetValue(h2, out var v2) is false)
{
instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T2>();
}
if (instances.TryGetValue(h3, out var v3) is false)
{
instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T3>();
}
if (instances.TryGetValue(h4, out var v4) is false)
{
instances[h4] = v4 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T4>();
}
array[i] = (Unsafe.As<T>(v0), Unsafe.As<T1>(v1), Unsafe.As<T2>(v2), Unsafe.As<T3>(v3),
Unsafe.As<T4>(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<T, T1, T2, T3, T4, T5>() 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<T>();
}
if (instances.TryGetValue(h1, out var v1) is false)
{
instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T1>();
}
if (instances.TryGetValue(h2, out var v2) is false)
{
instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T2>();
}
if (instances.TryGetValue(h3, out var v3) is false)
{
instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T3>();
}
if (instances.TryGetValue(h4, out var v4) is false)
{
instances[h4] = v4 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T4>();
}
if (instances.TryGetValue(h5, out var v5) is false)
{
instances[h5] = v5 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T5>();
}
array[i] = (Unsafe.As<T>(v0), Unsafe.As<T1>(v1), Unsafe.As<T2>(v2), Unsafe.As<T3>(v3),
Unsafe.As<T4>(v4), Unsafe.As<T5>(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<T, T1, T2, T3, T4, T5, T6>() 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<T>();
}
if (instances.TryGetValue(h1, out var v1) is false)
{
instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T1>();
}
if (instances.TryGetValue(h2, out var v2) is false)
{
instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T2>();
}
if (instances.TryGetValue(h3, out var v3) is false)
{
instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T3>();
}
if (instances.TryGetValue(h4, out var v4) is false)
{
instances[h4] = v4 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T4>();
}
if (instances.TryGetValue(h5, out var v5) is false)
{
instances[h5] = v5 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T5>();
}
if (instances.TryGetValue(h6, out var v6) is false)
{
instances[h6] = v6 = _entitiesInternal[id].ServiceProvider.GetRequiredService<T6>();
}
array[i] = (Unsafe.As<T>(v0), Unsafe.As<T1>(v1), Unsafe.As<T2>(v2), Unsafe.As<T3>(v3),
Unsafe.As<T4>(v4), Unsafe.As<T5>(v5), Unsafe.As<T6>(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);
}
}
}

View File

@@ -18,6 +18,7 @@ namespace BITKit.Entities
IServiceProvider ServiceProvider { get; }
IServiceCollection ServiceCollection { get; }
void Inject(object obj);
}
/// <summary>
/// 基本实体服务
@@ -66,19 +67,13 @@ namespace BITKit.Entities
/// 通过Id获取或添加Entity
/// </summary>
IEntity GetOrAdd(int id,Func<int,IEntity> factory);
/// <summary>
/// 查询Entity,例如
/// </summary>
/// <para>var rotationEntities=EntitiesService.Query&lt;RotationComponent&gt;</para>
IEntity[] Query<T>();
/// <summary>
/// 查询1个组件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T[] QueryComponents<T>();
Span<T> QueryComponents<T>() where T : class;
/// <summary>
/// 查询2个组件
@@ -86,7 +81,7 @@ namespace BITKit.Entities
/// <typeparam name="T"></typeparam>
/// <typeparam name="T1"></typeparam>
/// <returns></returns>
ValueTuple<T, T1>[] QueryComponents<T, T1>();
Span<ValueTuple<T, T1>> QueryComponents<T, T1>() where T : class where T1 : class;
/// <summary>
/// 查询3个组件
@@ -95,7 +90,7 @@ namespace BITKit.Entities
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <returns></returns>
ValueTuple<T, T1, T2>[] QueryComponents<T, T1, T2>();
Span<ValueTuple<T, T1, T2>> QueryComponents<T, T1, T2>() where T : class where T1 : class where T2 : class;
/// <summary>
/// 查询4个组件
@@ -105,7 +100,7 @@ namespace BITKit.Entities
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <returns></returns>
ValueTuple<T, T1, T2, T3>[] QueryComponents<T, T1, T2, T3>();
Span<ValueTuple<T, T1, T2, T3>> QueryComponents<T, T1, T2, T3>() where T : class where T1 : class where T2 : class where T3:class;
/// <summary>
/// 查询5个组件
@@ -116,7 +111,7 @@ namespace BITKit.Entities
/// <typeparam name="T3"></typeparam>
/// <typeparam name="T4"></typeparam>
/// <returns></returns>
ValueTuple<T, T1, T2, T3, T4>[] QueryComponents<T, T1, T2, T3, T4>();
Span<ValueTuple<T, T1, T2, T3, T4>> QueryComponents<T, T1, T2, T3, T4>()where T : class where T1 : class where T2 : class where T3:class where T4:class;
/// <summary>
/// 查询6个组件
/// </summary>
@@ -127,7 +122,7 @@ namespace BITKit.Entities
/// <typeparam name="T4"></typeparam>
/// <typeparam name="T5"></typeparam>
/// <returns></returns>
ValueTuple<T, T1, T2, T3, T4, T5>[] QueryComponents<T, T1, T2, T3, T4, T5>();
Span<ValueTuple<T, T1, T2, T3, T4, T5>> QueryComponents<T, T1, T2, T3, T4, T5>()where T : class where T1 : class where T2 : class where T3:class where T4:class where T5 : class;
/// <summary>
/// 查询7个组件
@@ -140,7 +135,7 @@ namespace BITKit.Entities
/// <typeparam name="T5"></typeparam>
/// <typeparam name="T6"></typeparam>
/// <returns></returns>
ValueTuple<T, T1, T2, T3, T4, T5, T6>[] QueryComponents<T, T1, T2, T3, T4, T5, T6>();
Span<ValueTuple<T, T1, T2, T3, T4, T5, T6>> QueryComponents<T, T1, T2, T3, T4, T5, T6>()where T : class where T1 : class where T2 : class where T3:class where T4:class where T5 : class where T6 :class;
/// <summary>
/// 查询8个组件
@@ -154,6 +149,6 @@ namespace BITKit.Entities
/// <typeparam name="T6"></typeparam>
/// <typeparam name="TRest">剩余实例</typeparam>
/// <returns></returns>
ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>[] QueryComponents<T, T1, T2, T3, T4, T5, T6, TRest>() where TRest : struct;
//Span<ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>> QueryComponents<T, T1, T2, T3, T4, T5, T6, TRest>() where TRest : struct;
}
}

View File

@@ -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<Component>())
{
if(!component)continue;
var type = component.GetType();
entity.ServiceCollection.AddSingleton(type, component);
foreach (var x in type.GetInterfaces())
{

View File

@@ -122,44 +122,20 @@ namespace BITKit
value = default;
return default;
}
[Obsolete("Use TryGetElementAt instead")]
public static bool TryGet<T>(this IEnumerable<T> self, int index, out T value)=>TryGetElementAt(self, index, out value);
public static bool TryInsert<TKey, TValue>(this IDictionary<TKey, TValue> self, TKey key, TValue value)
public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> 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<TKey, TValue>(this IDictionary<TKey, TValue> self, TKey key, TValue value)
{
TryInsert(self, key, value);
}
public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> self, TKey t)
{
lock (self)
{
if (self.TryGetValue(t, out TValue value))
{
}
else
{
value = Activator.CreateInstance<TValue>();
self.Add(t, value);
}
return value;
}
else
{
value = new TValue();
self.TryAdd(t, value);
}
return value;
}
public static bool TryRemove<T>(this IList<T> self, T t)
{
if (self.Contains(t))

View File

@@ -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<int,ConcurrentDictionary<int,object>> Services = new();
public static bool QueryComponents<T>(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<T>();
Services.GetOrCreate(id).TryAdd(typeId,value);
}
if (value is null)
{
t1 = null;
return false;
}
t1 = Unsafe.As<T>(value);
return true;
}
public static bool QueryComponents<T,T1> (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<T,T1,T2> (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<T,T1,T2,T3> (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<T>(this T t, Action action)
{
if (t is null)

View File

@@ -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<T> : IEntryGroup where T : IEntryElement
{
public int index = -1;
public List<T> list = new();
private int m_index = -1;
private bool completed=true;
public event Action<T> OnEntry;
public event Action<T> 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<T,bool> 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
}
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c9f4b5601bd64124a9efdee9cf9c7226
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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<CsvLocalizationService> _logger;
private readonly ILocalizationService _localizationService;
public CsvLocalizationService(ILocalizationService localizationService, ILogger<CsvLocalizationService> logger)
{
_localizationService = localizationService;
_logger = logger;
_localizationService.OnLanguageChangeAsync += OnLanguageChangeAsync;
}
public static Dictionary<string, Dictionary<string, string>> ParseCsvToDictionary(string csvData)
{
var dict = new Dictionary<string, Dictionary<string, string>>();
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, string>());
}
}
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<string, string>();
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<string, string>();
_localizationService.LocalizedStrings.Add(lang,currentDictionary);
}
foreach (var (key, value) in dictionary)
{
currentDictionary.Set(key, value);
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 407c6cf54ae8abc42b77a090f9fa7616
guid: 0a0edc8f5d75b3646a7b405ef830a52d
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -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
{
/// <summary>
/// 本地化
/// </summary>
public interface ILocalizationService
{
public string Prefix { get; }
/// <summary>
/// 当前语言
/// </summary>
public string CurrentLanguage { get; }
/// <summary>
/// 更改回调,通常在此检查并下载语言包
/// </summary>
public event Func<string,string,UniTask> OnLanguageChangeAsync;
/// <summary>
/// 语言更改完成回调,UI和World执行更新
/// </summary>
public event Action<string,string> OnLanguageChanged;
/// <summary>
/// 获取翻译文本,返回无复制的引用
/// </summary>
/// <param name="key"></param>
/// <param name="language">语言,默认为当前CultureInfo.Name</param>
/// <returns></returns>
public string GetLocalizedString(string key,string language=null);
public UniTask ChangeLanguageAsync(string newLanguage);
public IDictionary<string, IDictionary<string, string>> 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<string, string, UniTask> OnLanguageChangeAsync;
public event Action<string, string> OnLanguageChanged;
private readonly ILogger<LocalizationService> _logger;
public IDictionary<string, IDictionary<string, string>> LocalizedStrings { get; } =
new Dictionary<string, IDictionary<string, string>>();
private readonly ValidHandle _isBusy = new();
private readonly HashSet<string> _untranslatedKeys = new();
public LocalizationService(ILogger<LocalizationService> 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();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: baab5c8590ad6ee44acbc258eeacdc8b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -19,6 +19,7 @@ namespace BITKit.Mod
{
list.Add((T)obj);
}
}
return list;
}

8
Src/Core/Quadtree.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c15a9900918e4324991bcf53015b006e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1193c2664d97cc049a6e4c486c6bce71
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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<int, float2> Positions
{
get
{
Expansion();
return _positions;
}
}
public IDictionary<int, float2> Sizes => _sizes;
private readonly Dictionary<int, float2> _positions;
private readonly ConcurrentQueue<(int, float2)> _queue;
private readonly Dictionary<int, float2> _sizes;
public Quadtree(float2 center, float2 size)
{
_sizes = new Dictionary<int, float2>();
_positions = new Dictionary<int, float2>();
_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<int> Query(float2 position, float radius)
{
Expansion();
var index = 0;
var root = Root;
var pool = ArrayPool<int>.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<int> 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<int>();
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
};
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b421b3302ecaaab4bab9eebf970e2bb6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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<int> Objects; // 存储的对象ID
public QuadtreeNode(Quadtree root,float2 center, float2 size)
{
Root = root;
Center = center;
Size = size;
Children = new QuadtreeNode[4];
Objects = new HashSet<int>();
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; // 如果子节点未初始化,说明是叶子节点
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 837e58f0e4d63c2438b1037a76a5a6ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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");

View File

@@ -35,7 +35,25 @@ namespace BITKit.Tween
return new TweenSequence();
}
public static async UniTask Lerp<T>(Action<T> setter,T from,T to,float duration, Func<T, T,float, T> lerp,CancellationToken cancellationToken = default)
public static async UniTask MoveToForward<T>(Action<T> setter, T from, T to, float delta,
Func<T, T, float, T> 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<T>(Action<T> setter,T from,T to,float duration, Func<T, T,float, T> 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

View File

@@ -7,7 +7,6 @@ namespace BITKit.UX
public interface IUXDialogue
{
void Show(string content,string title = "Alert",Action confirmAction=null,Action<bool> onChoose=null);
}
}

View File

@@ -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
/// <para>⭐异步打开与关闭</para>
/// <para>⭐当前可见状态</para>
/// <para>⭐基本UI导航回调</para>
public interface IUXPanel:IEntryElement
public interface IUXPanel:IStateAsync
{
/// <summary>
/// 是否为窗口,用于覆盖其他面板

View File

@@ -79,9 +79,6 @@ namespace BITKit
var action = x as Action<T>;
action.Invoke(value);
});
if (value is not JToken)
Objects.TryInsert(key, value);
}
public static bool TryGetValue<T>(string key, out T value)
{

View File

@@ -19,7 +19,10 @@ namespace BITKit
var type = typeof(T);
if (type == typeof(string))
{
_value = string.Empty.As<T>();
if (string.Empty is T t)
{
_value = t;
}
return;
}
if(type.IsAbstract || type.IsInterface)return;

View File

@@ -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);
}