1
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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<RotationComponent></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;
|
||||
}
|
||||
}
|
@@ -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())
|
||||
{
|
||||
|
@@ -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))
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
8
Src/Core/Localization.meta
Normal file
8
Src/Core/Localization.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9f4b5601bd64124a9efdee9cf9c7226
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
92
Src/Core/Localization/CsvLocalizationService.cs
Normal file
92
Src/Core/Localization/CsvLocalizationService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 407c6cf54ae8abc42b77a090f9fa7616
|
||||
guid: 0a0edc8f5d75b3646a7b405ef830a52d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
113
Src/Core/Localization/ILocalizationService.cs
Normal file
113
Src/Core/Localization/ILocalizationService.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
11
Src/Core/Localization/ILocalizationService.cs.meta
Normal file
11
Src/Core/Localization/ILocalizationService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: baab5c8590ad6ee44acbc258eeacdc8b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -19,6 +19,7 @@ namespace BITKit.Mod
|
||||
{
|
||||
list.Add((T)obj);
|
||||
}
|
||||
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
8
Src/Core/Quadtree.meta
Normal file
8
Src/Core/Quadtree.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c15a9900918e4324991bcf53015b006e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
20
Src/Core/Quadtree/Net.BITKit.Quadtree.asmdef
Normal file
20
Src/Core/Quadtree/Net.BITKit.Quadtree.asmdef
Normal 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
|
||||
}
|
7
Src/Core/Quadtree/Net.BITKit.Quadtree.asmdef.meta
Normal file
7
Src/Core/Quadtree/Net.BITKit.Quadtree.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1193c2664d97cc049a6e4c486c6bce71
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
305
Src/Core/Quadtree/Quadtree.cs
Normal file
305
Src/Core/Quadtree/Quadtree.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Src/Core/Quadtree/Quadtree.cs.meta
Normal file
11
Src/Core/Quadtree/Quadtree.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b421b3302ecaaab4bab9eebf970e2bb6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
84
Src/Core/Quadtree/QuadtreeNode.cs
Normal file
84
Src/Core/Quadtree/QuadtreeNode.cs
Normal 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; // 如果子节点未初始化,说明是叶子节点
|
||||
}
|
||||
}
|
||||
}
|
11
Src/Core/Quadtree/QuadtreeNode.cs.meta
Normal file
11
Src/Core/Quadtree/QuadtreeNode.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 837e58f0e4d63c2438b1037a76a5a6ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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");
|
||||
|
@@ -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
|
||||
|
@@ -7,7 +7,6 @@ namespace BITKit.UX
|
||||
public interface IUXDialogue
|
||||
{
|
||||
void Show(string content,string title = "Alert",Action confirmAction=null,Action<bool> onChoose=null);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
/// 是否为窗口,用于覆盖其他面板
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user