1
This commit is contained in:
@@ -240,6 +240,7 @@ namespace BITKit
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern bool SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
|
||||
public static string AppName { get; private set; } = nameof(BITApp);
|
||||
public static UniTaskCompletionSource WalkUntilInitialize { get; set; } = new();
|
||||
private static CancellationTokenSource CancellationTokenSource = new();
|
||||
public static CancellationToken CancellationToken => CancellationTokenSource.Token;
|
||||
public static InitializationState State;
|
||||
|
@@ -13,28 +13,49 @@ namespace BITKit.Entities
|
||||
public class EntitiesService:IEntitiesService,IDisposable
|
||||
{
|
||||
private readonly ILogger<EntitiesService> _logger;
|
||||
|
||||
private readonly IFixedTicker _ticker;
|
||||
private static int _count;
|
||||
public EntitiesService()
|
||||
{
|
||||
_count++;
|
||||
}
|
||||
|
||||
public EntitiesService(ILogger<EntitiesService> logger)
|
||||
private static readonly ConcurrentQueue<IEntity> OnAddQueue = new();
|
||||
private static ConcurrentDictionary<int, HashSet<int>> TypeCaches = new();
|
||||
public EntitiesService(ILogger<EntitiesService> logger, IFixedTicker ticker)
|
||||
{
|
||||
if (_count > 0)
|
||||
{
|
||||
throw new MulticastNotSupportedException();
|
||||
}
|
||||
_count++;
|
||||
|
||||
_logger = logger;
|
||||
_ticker = ticker;
|
||||
|
||||
_ticker.Add(OnTick);
|
||||
}
|
||||
|
||||
private void OnTick(float obj)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static readonly ConcurrentDictionary<int, IEntity> Entities = new();
|
||||
public event Action<IEntity> OnAdd;
|
||||
public event Action<IEntity> OnRemove;
|
||||
IEntity[] IEntitiesService.Entities => Entities.Values.ToArray();
|
||||
IReadOnlyDictionary<int, IEntity> IEntitiesService.Entities => Entities;
|
||||
public bool Register(IEntity entity)
|
||||
{
|
||||
if (!Entities.TryAdd(entity.Id, entity)) return false;
|
||||
OnAdd?.Invoke(entity);
|
||||
OnAddQueue.Enqueue(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,8 +63,17 @@ namespace BITKit.Entities
|
||||
{
|
||||
if (!Entities.TryRemove(entity.Id, out _)) return false;
|
||||
OnRemove?.Invoke(entity);
|
||||
|
||||
foreach (var serviceDescriptor in entity.ServiceCollection)
|
||||
{
|
||||
var typeHash = serviceDescriptor.ServiceType.GetHashCode();
|
||||
var hashSet = TypeCaches.GetOrCreate(typeHash);
|
||||
hashSet.Remove(entity.Id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
public IEntity Get(int id)
|
||||
@@ -262,6 +292,8 @@ namespace BITKit.Entities
|
||||
_logger.LogInformation($"已释放,还剩{_count}个实例");
|
||||
|
||||
_cancellationTokenSource?.Dispose();
|
||||
|
||||
_ticker.Remove(OnTick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,24 +8,24 @@ namespace BITKit.Entities
|
||||
{
|
||||
public class Entity : IEntity, IDisposable
|
||||
{
|
||||
public Entity()
|
||||
{
|
||||
ServiceCollection.AddSingleton<IEntity>(this);
|
||||
}
|
||||
public void WaitForInitializationComplete()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public int Id { get; set; } = Guid.NewGuid().GetHashCode();
|
||||
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
|
||||
public IServiceProvider ServiceProvider => _serviceProvider ??= ServiceCollection.BuildServiceProvider();
|
||||
private ServiceProvider _serviceProvider;
|
||||
public IServiceCollection ServiceCollection { get; } = new ServiceCollection();
|
||||
public IServiceCollection ServiceCollection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_serviceCollection is not null) return _serviceCollection;
|
||||
_serviceCollection = new ServiceCollection();
|
||||
_serviceCollection.AddSingleton<IEntity>(this);
|
||||
return _serviceCollection;
|
||||
}
|
||||
}
|
||||
|
||||
private IServiceCollection _serviceCollection;
|
||||
|
||||
public object[] GetServices() => ServiceCollection.ToArray()
|
||||
.Select(x => _serviceProvider.GetService(x.ServiceType)).ToArray();
|
||||
public void Inject(object obj)
|
||||
{
|
||||
foreach (var fieldInfo in obj.GetType().GetFields(ReflectionHelper.Flags))
|
||||
@@ -39,7 +39,6 @@ namespace BITKit.Entities
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
_serviceProvider.Dispose();
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
#if NET5_0_OR_GREATER
|
||||
@@ -16,7 +17,6 @@ namespace BITKit.Entities
|
||||
CancellationToken CancellationToken { get; }
|
||||
IServiceProvider ServiceProvider { get; }
|
||||
IServiceCollection ServiceCollection { get; }
|
||||
object[] GetServices();
|
||||
void Inject(object obj);
|
||||
}
|
||||
/// <summary>
|
||||
@@ -35,7 +35,7 @@ namespace BITKit.Entities
|
||||
/// <summary>
|
||||
/// 所有Entity
|
||||
/// </summary>
|
||||
IEntity[] Entities { get; }
|
||||
IReadOnlyDictionary<int,IEntity> Entities { get; }
|
||||
/// <summary>
|
||||
/// 注册Entity
|
||||
/// </summary>
|
||||
|
106
Src/Core/ECS/UnityEntity.cs
Normal file
106
Src/Core/ECS/UnityEntity.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace BITKit.Entities
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
[DefaultExecutionOrder(-1)]
|
||||
public class UnityEntity : MonoBehaviour,IEntity
|
||||
{
|
||||
private IEntitiesService _entitiesService;
|
||||
private IEntity _entity;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_entitiesService = BITApp.ServiceProvider.GetRequiredService<IEntitiesService>();
|
||||
|
||||
if (_entitiesService.Entities.ContainsKey(gameObject.GetInstanceID())) return;
|
||||
|
||||
var entity = new Entity()
|
||||
{
|
||||
Id = gameObject.GetInstanceID(),
|
||||
CancellationToken = destroyCancellationToken
|
||||
};
|
||||
|
||||
var idComponent = new IdComponent()
|
||||
{
|
||||
Id = entity.Id,
|
||||
Name = gameObject.name,
|
||||
};
|
||||
|
||||
entity.ServiceCollection.AddSingleton(idComponent);
|
||||
|
||||
foreach (var component in GetComponents<Component>())
|
||||
{
|
||||
var type = component.GetType();
|
||||
|
||||
foreach (var x in type.GetInterfaces())
|
||||
{
|
||||
entity.ServiceCollection.AddSingleton(x, component);
|
||||
}
|
||||
|
||||
while (type is not null)
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
try
|
||||
{
|
||||
switch (baseType)
|
||||
{
|
||||
case null:
|
||||
case not null when baseType == typeof(object):
|
||||
case not null when baseType == typeof(Object):
|
||||
case not null when baseType == typeof(MonoBehaviour):
|
||||
case not null when baseType == typeof(Behaviour):
|
||||
case not null when baseType == typeof(Component):
|
||||
case not null when baseType == typeof(Component):
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
entity.ServiceCollection.AddSingleton(baseType, component);
|
||||
type = type.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
entity.ServiceCollection.AddSingleton(gameObject);
|
||||
entity.ServiceCollection.AddSingleton(transform);
|
||||
|
||||
destroyCancellationToken.Register(Dispose);
|
||||
|
||||
_entity = entity;
|
||||
|
||||
_entitiesService.Register(entity);
|
||||
}
|
||||
|
||||
private void Dispose()
|
||||
{
|
||||
_entitiesService?.UnRegister(_entity);
|
||||
}
|
||||
|
||||
public int Id => _entity.Id;
|
||||
|
||||
public CancellationToken CancellationToken => _entity.CancellationToken;
|
||||
|
||||
public IServiceProvider ServiceProvider => _entity.ServiceProvider;
|
||||
|
||||
public IServiceCollection ServiceCollection => _entity.ServiceCollection;
|
||||
|
||||
public void Inject(object obj)
|
||||
{
|
||||
_entity.Inject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca82e09109a03de47b638f35b49b59e5
|
||||
guid: 230e015069b45484f9c85d1aba1e901c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@@ -15,6 +15,9 @@ namespace BITKit
|
||||
/// </summary>
|
||||
public interface IRuntimeItemContainer:IBinarySerialize
|
||||
{
|
||||
public bool AllowAdd { get; set; }
|
||||
public bool AllowRemove { get; set; }
|
||||
|
||||
public ValidHandle IsBusy { get; }
|
||||
/// <summary>
|
||||
/// 物品容器的唯一Id
|
||||
@@ -96,6 +99,8 @@ namespace BITKit
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool AllowAdd { get; set; }
|
||||
public bool AllowRemove { get; set; }
|
||||
public ValidHandle IsBusy { get; } = new();
|
||||
public int Id { get; set; }
|
||||
public IRuntimeItem[] GetItems()=>Items.Values.ToArray();
|
||||
|
@@ -9,13 +9,16 @@ namespace BITKit.Pool
|
||||
/// </summary>
|
||||
public interface IPoolService
|
||||
{
|
||||
public int DefaultCapacity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成对象
|
||||
/// </summary>
|
||||
/// <param name="path">可寻址路径</param>
|
||||
/// <param name="prefab">直接提供的预制体</param>
|
||||
/// <typeparam name="T">类型</typeparam>
|
||||
/// <returns></returns>
|
||||
UniTask<T> Spawn<T>(string path) where T : class;
|
||||
UniTask<T> Spawn<T>(string path,object prefab=null) where T : class;
|
||||
/// <summary>
|
||||
/// 回收对象
|
||||
/// </summary>
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@@ -24,7 +25,6 @@ namespace BITKit.StateMachine
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
public T CurrentState { get;private set; }
|
||||
public T NextOrCurrentState => _nextTargetState.IfNotAllow(CurrentState);
|
||||
public event Action<T, T> OnStateChanging;
|
||||
public event Func<T, T, UniTask> OnStateChangeAsync;
|
||||
public event Action<T, T> OnStateChanged;
|
||||
@@ -35,7 +35,13 @@ namespace BITKit.StateMachine
|
||||
public readonly ValidHandle IsBusy=new();
|
||||
private readonly CancellationTokenSource _cancellationTokenSource=new();
|
||||
private readonly Dictionary<int, T> _dictionary = new();
|
||||
private readonly Optional<T> _nextTargetState = new();
|
||||
|
||||
private readonly HashSet<int> _isRegistered = new();
|
||||
|
||||
private CancellationTokenSource _transitionCts;
|
||||
|
||||
private T _entryCompletedState;
|
||||
|
||||
public async void Initialize()
|
||||
{
|
||||
await IsBusy;
|
||||
@@ -43,33 +49,27 @@ namespace BITKit.StateMachine
|
||||
using var _ = IsBusy.GetHandle();
|
||||
foreach (var (_,value) in StateDictionary)
|
||||
{
|
||||
await value.InitializeAsync();
|
||||
value.Initialize();
|
||||
if (_isRegistered.Add(value.Identifier))
|
||||
{
|
||||
await value.InitializeAsync();
|
||||
value.Initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async void UpdateState(float deltaTime)
|
||||
{
|
||||
if (CurrentState is null) return;
|
||||
if (_entryCompletedState is null) return;
|
||||
using var _ = IsBusy.GetHandle();
|
||||
CurrentState.OnStateUpdate(deltaTime);
|
||||
await CurrentState.OnStateUpdateAsync(deltaTime);
|
||||
_entryCompletedState.OnStateUpdate(deltaTime);
|
||||
await _entryCompletedState.OnStateUpdateAsync(deltaTime);
|
||||
}
|
||||
|
||||
|
||||
public async void DisposeState()
|
||||
public void DisposeState()
|
||||
{
|
||||
await IsBusy;
|
||||
if (_cancellationTokenSource.IsCancellationRequested) return;
|
||||
if (CurrentState is null) return;
|
||||
using var _ = IsBusy.GetHandle();
|
||||
CurrentState.Enabled = false;
|
||||
await CurrentState.OnStateExitAsync(CurrentState, null);
|
||||
CurrentState.OnStateExit(CurrentState, null);
|
||||
CurrentState = null;
|
||||
TransitionState(null);
|
||||
}
|
||||
|
||||
public T TransitionState<TState>() where TState : T
|
||||
{
|
||||
T nextState;
|
||||
@@ -96,50 +96,60 @@ namespace BITKit.StateMachine
|
||||
nextState.Identifier = nextState.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
if (Equals(nextState, CurrentState)) return;
|
||||
if(_nextTargetState.Allow && Equals(_nextTargetState.Value,nextState))return;
|
||||
if (_nextTargetState.Allow)
|
||||
{
|
||||
_nextTargetState.Value = nextState;
|
||||
return;
|
||||
}
|
||||
_nextTargetState.SetValueThenAllow(nextState);
|
||||
await IsBusy;
|
||||
|
||||
if(CurrentState==nextState)return;
|
||||
if(_cancellationTokenSource.IsCancellationRequested)return;
|
||||
using var _ = IsBusy.GetHandle();
|
||||
|
||||
OnStateChanging?.Invoke(CurrentState,nextState);
|
||||
await OnStateChangeAsync.UniTaskFunc(CurrentState,nextState);
|
||||
|
||||
if (nextState is not null && _dictionary.TryAdd(nextState.Identifier, nextState))
|
||||
{
|
||||
await nextState.InitializeAsync();
|
||||
if(_cancellationTokenSource.IsCancellationRequested)return;
|
||||
nextState.Initialize();
|
||||
}
|
||||
if (CurrentState is not null)
|
||||
{
|
||||
CurrentState.Enabled = false;
|
||||
await CurrentState.OnStateExitAsync(CurrentState, nextState);
|
||||
if(_cancellationTokenSource.IsCancellationRequested)return;
|
||||
CurrentState.OnStateExit(CurrentState,nextState);
|
||||
}
|
||||
|
||||
if (_nextTargetState.Allow && _nextTargetState.Value != nextState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var tempState = CurrentState;
|
||||
CurrentState = _nextTargetState.Value;
|
||||
_nextTargetState.Clear();
|
||||
nextState.Enabled = true;
|
||||
await nextState.OnStateEntryAsync(tempState);
|
||||
if(_cancellationTokenSource.IsCancellationRequested)return;
|
||||
nextState.OnStateEntry(tempState);
|
||||
OnStateChanged?.Invoke(tempState,nextState);
|
||||
|
||||
CurrentState = nextState;
|
||||
|
||||
_transitionCts?.Cancel();
|
||||
_transitionCts = new CancellationTokenSource();
|
||||
|
||||
var ct = _transitionCts.Token;
|
||||
|
||||
await IsBusy;
|
||||
using var _ = IsBusy.GetHandle();
|
||||
if(ct.IsCancellationRequested||_cancellationTokenSource.IsCancellationRequested)return;
|
||||
|
||||
OnStateChanging?.Invoke(tempState,nextState);
|
||||
|
||||
if (tempState is not null)
|
||||
{
|
||||
if (_entryCompletedState == tempState)
|
||||
{
|
||||
_entryCompletedState = null;
|
||||
}
|
||||
|
||||
tempState.Enabled = false;
|
||||
await tempState.OnStateExitAsync(tempState, nextState);
|
||||
tempState.OnStateExit(tempState,nextState);
|
||||
if(_cancellationTokenSource.IsCancellationRequested)return;
|
||||
}
|
||||
|
||||
if(ct.IsCancellationRequested)return;
|
||||
|
||||
await OnStateChangeAsync.UniTaskFunc(CurrentState,nextState);
|
||||
|
||||
if(ct.IsCancellationRequested)return;
|
||||
|
||||
if (nextState is not null)
|
||||
{
|
||||
if (_isRegistered.Add(nextState.Identifier))
|
||||
{
|
||||
await RegisterAsync(nextState);
|
||||
if(ct.IsCancellationRequested || _cancellationTokenSource.IsCancellationRequested)return;
|
||||
}
|
||||
|
||||
nextState.Enabled = true;
|
||||
await nextState.OnStateEntryAsync(CurrentState);
|
||||
nextState.OnStateEntry(CurrentState);
|
||||
if(ct.IsCancellationRequested || _cancellationTokenSource.IsCancellationRequested)return;
|
||||
|
||||
_entryCompletedState = nextState;
|
||||
}
|
||||
|
||||
OnStateChanged?.Invoke(tempState, nextState);
|
||||
|
||||
}
|
||||
public T TransitionState(T nextState)
|
||||
@@ -148,27 +158,49 @@ namespace BITKit.StateMachine
|
||||
return nextState;
|
||||
}
|
||||
|
||||
private async UniTask RegisterAsync(T newState)
|
||||
{
|
||||
StateDictionary.TryAdd(newState.GetType(),newState);
|
||||
|
||||
_dictionary.TryAdd(newState.Identifier, newState);
|
||||
|
||||
|
||||
newState.Initialize();
|
||||
await newState.InitializeAsync();
|
||||
}
|
||||
|
||||
public async void Register(T newState)
|
||||
{
|
||||
await IsBusy;
|
||||
using var _ = IsBusy.GetHandle();
|
||||
await RegisterAsync(newState);
|
||||
}
|
||||
public async void UnRegister(T newState)
|
||||
{
|
||||
if (newState is null) return;
|
||||
if (Dictionary.ContainsKey(newState.Identifier) is false) return;
|
||||
_dictionary.Remove(newState.Identifier);
|
||||
|
||||
await IsBusy;
|
||||
using var _ = IsBusy.GetHandle();
|
||||
|
||||
if (Equals(CurrentState, newState))
|
||||
{
|
||||
await CurrentState.OnStateExitAsync(CurrentState, null);
|
||||
CurrentState.OnStateExit(CurrentState, null);
|
||||
if (CurrentState is IAsyncDisposable asyncDisposable)
|
||||
{
|
||||
await asyncDisposable.DisposeAsync();
|
||||
}
|
||||
if (CurrentState is IDisposable disposable)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
CurrentState = null;
|
||||
}
|
||||
|
||||
if (newState is IAsyncDisposable asyncDisposable)
|
||||
{
|
||||
await asyncDisposable.DisposeAsync();
|
||||
}
|
||||
if (newState is IDisposable disposable)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
OnStateUnRegistered?.Invoke(newState);
|
||||
}
|
||||
public void Dispose()
|
||||
|
@@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
public interface ITag
|
||||
{
|
||||
int Hash { get; }
|
||||
string[] GetTags();
|
||||
IReadOnlyCollection<string> Tags { get; }
|
||||
}
|
||||
}
|
@@ -41,7 +41,7 @@ namespace BITKit
|
||||
/// <summary>
|
||||
/// 异步循环
|
||||
/// </summary>
|
||||
public interface IAsyncTicker
|
||||
public interface IAsyncTicker:IDisposable
|
||||
{
|
||||
ulong TickCount { get; }
|
||||
int TickRate { get; set; }
|
||||
@@ -183,6 +183,7 @@ namespace BITKit
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
OnTickAsync = null;
|
||||
_isDisposed = true;
|
||||
_timer.Stop();
|
||||
_timer.Dispose();
|
||||
|
@@ -35,26 +35,18 @@ namespace BITKit.Tween
|
||||
return new TweenSequence();
|
||||
}
|
||||
|
||||
public static async UniTask MoveToForward(
|
||||
Action<float> setter,
|
||||
float from,
|
||||
float to,
|
||||
float duration = 1,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
public static async UniTask Lerp<T>(Action<T> setter,T from,T to,float duration, Func<T, T,float, T> lerp,CancellationToken cancellationToken = default)
|
||||
{
|
||||
var t = 0f;
|
||||
var delta = 1 / duration;
|
||||
var delta = 1f / duration;
|
||||
setter(from);
|
||||
//BIT4Log.Log<TweenSequence>($"已创建Tween,from:[{from}]to:[{to}]duration:[{duration}]delta:[{delta}]");
|
||||
while (t < 1 && cancellationToken.IsCancellationRequested is false)
|
||||
{
|
||||
t = math.clamp(t + delta*BITApp.Time.DeltaTime, 0, 1);
|
||||
var next = math.lerp(from, to, t);
|
||||
//BIT4Log.Log<TweenSequence>($"当前进度:[{t}]next:[{next}]");
|
||||
var next = lerp(from, to, t);
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
await UniTask.NextFrame(cancellationToken);
|
||||
await UniTask.SwitchToMainThread(cancellationToken);
|
||||
#else
|
||||
await UniTask.Yield();
|
||||
#endif
|
||||
|
@@ -7,5 +7,7 @@ namespace BITKit.UX
|
||||
public interface IUXDialogue
|
||||
{
|
||||
void Show(string content,string title = "Alert",Action confirmAction=null,Action<bool> onChoose=null);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -42,13 +42,9 @@ namespace BITKit
|
||||
public static implicit operator T(References<T> self) => self.Get();
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public record Reference : References
|
||||
[Serializable]
|
||||
public struct Reference : IReference
|
||||
{
|
||||
public Reference()
|
||||
{
|
||||
}
|
||||
|
||||
public Reference(string value)
|
||||
{
|
||||
this.value = value;
|
||||
@@ -57,8 +53,7 @@ namespace BITKit
|
||||
[UnityEngine.TextArea]
|
||||
#endif
|
||||
public string value;
|
||||
public override string Get() => value;
|
||||
public override string ToString() => value;
|
||||
public string Get() => value;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
|
@@ -27,7 +27,7 @@ namespace BITKit
|
||||
public class MyHandle:IDisposable
|
||||
{
|
||||
private readonly ValidHandle _validHandle;
|
||||
private bool _isDisable = false;
|
||||
private readonly bool _isDisable;
|
||||
public MyHandle(ValidHandle validHandle,bool isDisable = false)
|
||||
{
|
||||
_validHandle = validHandle;
|
||||
@@ -61,106 +61,76 @@ namespace BITKit
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Allow:{enableHandle}\nElements:{string.Join("\n",objs)}\nDisableElements:{string.Join("\n",disableObjs)}";
|
||||
return $"Allow:{_enableHandle}\nElements:{string.Join("\n",_objs)}\nDisableElements:{string.Join("\n",_disableObjs)}";
|
||||
}
|
||||
|
||||
public ValidHandle() {}
|
||||
public ValidHandle(Action<bool> boolDelegate)
|
||||
{
|
||||
AddListener(boolDelegate);
|
||||
EventOnEnableChanged?.Invoke(enableHandle);
|
||||
_eventOnEnableChanged?.Invoke(_enableHandle);
|
||||
}
|
||||
public static implicit operator bool(ValidHandle validHandle)
|
||||
{
|
||||
return !validHandle._isDisposed && validHandle.enableHandle;
|
||||
return !validHandle._isDisposed && validHandle._enableHandle;
|
||||
}
|
||||
|
||||
public bool Allow => this;
|
||||
|
||||
private bool enableHandle;
|
||||
private bool _enableHandle;
|
||||
/// <summary>
|
||||
/// ⚠️Dont operate this field directly
|
||||
/// </summary>
|
||||
public readonly List<object> objs = new List<object>();
|
||||
private readonly HashSet<object> _objs = new();
|
||||
|
||||
/// <summary>
|
||||
/// ⚠️Dont operate this field directly
|
||||
/// </summary>
|
||||
public readonly List<object> disableObjs = new List<object>();
|
||||
private bool tempEnable;
|
||||
private Action<bool> EventOnEnableChanged;
|
||||
private readonly HashSet<object> _disableObjs = new();
|
||||
private bool _tempEnable;
|
||||
private Action<bool> _eventOnEnableChanged;
|
||||
private readonly ConcurrentQueue<UniTaskCompletionSource> _completionSources = new();
|
||||
private bool _isDisposed;
|
||||
|
||||
public void AddElement(object obj)
|
||||
{
|
||||
if (objs.Contains(obj))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
objs.Add(obj);
|
||||
}
|
||||
_objs.Add(obj);
|
||||
CheckEnable();
|
||||
}
|
||||
|
||||
private void CheckEnable()
|
||||
{
|
||||
tempEnable = objs.Count > 0 && disableObjs.Count == 0;
|
||||
if (tempEnable != enableHandle)
|
||||
_tempEnable = _objs.Count > 0 && _disableObjs.Count == 0;
|
||||
if (_tempEnable == _enableHandle) return;
|
||||
_enableHandle = _tempEnable;
|
||||
_eventOnEnableChanged?.Invoke(_enableHandle);
|
||||
if (_tempEnable) return;
|
||||
if (_completionSources.TryDequeue(out var cs))
|
||||
{
|
||||
enableHandle = tempEnable;
|
||||
if (EventOnEnableChanged is not null)
|
||||
{
|
||||
EventOnEnableChanged.Invoke(enableHandle);
|
||||
}
|
||||
if (tempEnable) return;
|
||||
if (_completionSources.TryDequeue(out var cs))
|
||||
{
|
||||
cs.TrySetResult();
|
||||
}
|
||||
cs.TrySetResult();
|
||||
}
|
||||
}
|
||||
public void RemoveElement(object obj)
|
||||
{
|
||||
if (objs.Contains(obj))
|
||||
if (_objs.Contains(obj))
|
||||
{
|
||||
objs.Remove(obj);
|
||||
_objs.Remove(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
CheckEnable();
|
||||
}
|
||||
public int lenght => objs.Count;
|
||||
public string[] GetElements()
|
||||
{
|
||||
List<string> elementNames = new List<string>();
|
||||
for (int i = 0; i < objs.Count; i++)
|
||||
{
|
||||
elementNames.Add(objs[i].ToString());
|
||||
}
|
||||
return elementNames.ToArray();
|
||||
}
|
||||
public bool Contains(object obj) => objs.Contains(obj);
|
||||
public int Lenght => _objs.Count;
|
||||
public bool Contains(object obj) => _objs.Contains(obj);
|
||||
public void AddDisableElements(object obj)
|
||||
{
|
||||
if (disableObjs.Contains(obj))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
disableObjs.Add(obj);
|
||||
}
|
||||
_disableObjs.Add(obj);
|
||||
CheckEnable();
|
||||
}
|
||||
public void RemoveDisableElements(object obj)
|
||||
{
|
||||
if (disableObjs.Contains(obj))
|
||||
if (_disableObjs.Contains(obj))
|
||||
{
|
||||
disableObjs.Remove(obj);
|
||||
_disableObjs.Remove(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -191,22 +161,22 @@ namespace BITKit
|
||||
}
|
||||
public void Invoke()
|
||||
{
|
||||
var enable = disableObjs.Count == 0 && objs.Count > 0;
|
||||
EventOnEnableChanged?.Invoke(enable);
|
||||
var enable = _disableObjs.Count == 0 && _objs.Count > 0;
|
||||
_eventOnEnableChanged?.Invoke(enable);
|
||||
}
|
||||
public void Invoke(bool value)
|
||||
{
|
||||
EventOnEnableChanged?.Invoke(value);
|
||||
_eventOnEnableChanged?.Invoke(value);
|
||||
}
|
||||
public void AddListener(Action<bool> action)
|
||||
{
|
||||
EventOnEnableChanged+= action;
|
||||
_eventOnEnableChanged+= action;
|
||||
}
|
||||
public void RemoveListener(Action<bool> action)
|
||||
{
|
||||
if(EventOnEnableChanged is not null && action is not null)
|
||||
if(_eventOnEnableChanged is not null && action is not null)
|
||||
{
|
||||
EventOnEnableChanged -= action;
|
||||
_eventOnEnableChanged -= action;
|
||||
}
|
||||
}
|
||||
public UniTask.Awaiter GetAwaiter()
|
||||
@@ -221,17 +191,17 @@ namespace BITKit
|
||||
}
|
||||
public void Clear()
|
||||
{
|
||||
objs.Clear();
|
||||
disableObjs.Clear();
|
||||
_objs.Clear();
|
||||
_disableObjs.Clear();
|
||||
Invoke();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_isDisposed = true;
|
||||
objs.Clear();
|
||||
disableObjs.Clear();
|
||||
EventOnEnableChanged = null;
|
||||
_objs.Clear();
|
||||
_disableObjs.Clear();
|
||||
_eventOnEnableChanged = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,12 +8,6 @@ namespace BITKit.WorldNode
|
||||
/// </summary>
|
||||
public interface IWorldNode
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public object WorldObject { get; set; }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit.WorldNode
|
||||
{
|
||||
/// <summary>
|
||||
/// 世界节点服务,所有动态世界节点通过此接口注册
|
||||
/// </summary>
|
||||
public interface IWorldNodeService
|
||||
{
|
||||
public IReadOnlyDictionary<int, HashSet<IWorldNode>> WorldNodes { get; }
|
||||
public void RegisterNode(IWorldNode node);
|
||||
public event Action<IWorldNode> OnNodeRegistered;
|
||||
}
|
||||
/// <summary>
|
||||
/// 世界节点默认实现
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class WorldNodeService : IWorldNodeService,IDisposable
|
||||
{
|
||||
public static event Action<IWorldNode> OnNodeRegistered;
|
||||
IReadOnlyDictionary<int, HashSet<IWorldNode>> IWorldNodeService.WorldNodes => WorldNodes;
|
||||
private static readonly ConcurrentDictionary<int, HashSet<IWorldNode>> WorldNodes = new();
|
||||
public void RegisterNode(IWorldNode node)
|
||||
{
|
||||
OnNodeRegistered?.Invoke(node);
|
||||
WorldNodes.GetOrCreate(node.Id).Add(node);
|
||||
}
|
||||
event Action<IWorldNode> IWorldNodeService.OnNodeRegistered
|
||||
{
|
||||
add=>OnNodeRegistered+=value;
|
||||
remove=>OnNodeRegistered-=value;
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
WorldNodes.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,37 +2,30 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using BITKit.Entities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace BITKit.WorldNode
|
||||
{
|
||||
public sealed class UnityNode : MonoBehaviour,IWorldNode
|
||||
[RequireComponent(typeof(UnityEntity))]
|
||||
public sealed class UnityNode : MonoBehaviour
|
||||
{
|
||||
[SerializeReference, SubclassSelector] private IWorldNodeService worldNodeService = new WorldNodeService();
|
||||
[SerializeReference, SubclassSelector] private IWorldNode worldNode;
|
||||
public int Id { get; set; }
|
||||
|
||||
public IWorldNode WorldNode => worldNode;
|
||||
|
||||
public object WorldObject
|
||||
{
|
||||
get => gameObject;
|
||||
set=>throw new InvalidOperationException("Cannot set WorldObject");
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
if (worldNode is null)
|
||||
if(worldNode is null)return;
|
||||
|
||||
var entity = GetComponent<IEntity>();
|
||||
var type = worldNode.GetType();
|
||||
GetComponent<IEntity>().ServiceCollection.AddSingleton(type,worldNode);
|
||||
foreach (var interfaceType in type.GetInterfaces())
|
||||
{
|
||||
Debug.LogWarning("WorldNode is null");
|
||||
return;
|
||||
entity.ServiceCollection.AddSingleton(interfaceType, worldNode);
|
||||
}
|
||||
Id = gameObject.GetInstanceID();
|
||||
worldNode.Id = Id;
|
||||
worldNode.WorldObject = gameObject;
|
||||
worldNode.Initialize();
|
||||
worldNodeService.RegisterNode(worldNode);
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
Src/Core/WorldNode/WorldInfoNode.cs
Normal file
33
Src/Core/WorldNode/WorldInfoNode.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
using UnityEngine;
|
||||
#endif
|
||||
namespace BITKit.WorldNode
|
||||
{
|
||||
[Serializable]
|
||||
public class WorldInfoNode : IWorldNode
|
||||
{
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
[SerializeReference, SubclassSelector]
|
||||
|
||||
private IReference name;
|
||||
[SerializeReference, SubclassSelector]
|
||||
private IReference description;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => name?.Value;
|
||||
set => name = new Reference(value);
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get => description?.Value;
|
||||
set=>description = new Reference(value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 132a27b99db1e664692937ec23f40676
|
||||
guid: 5e1cbe087263f1342abbc3c6fb5c4068
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@@ -1,60 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
using UnityEngine;
|
||||
#endif
|
||||
namespace BITKit.WorldNode
|
||||
{
|
||||
[Serializable]
|
||||
public struct WorldInfoNode : IWorldNode
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
[SerializeReference,SubclassSelector]
|
||||
#endif
|
||||
private IReference name;
|
||||
// ReSharper disable once InconsistentNaming
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
[SerializeReference,SubclassSelector]
|
||||
#endif
|
||||
private IReference description;
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
public void Initialize()
|
||||
{
|
||||
Name = name?.Value;
|
||||
Description = description?.Value;
|
||||
}
|
||||
#endif
|
||||
public int Id { get; set; }
|
||||
public object WorldObject { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
public sealed class WorldInfoNodeService : IDisposable
|
||||
{
|
||||
public IReadOnlyDictionary<int, WorldInfoNode> WorldInfoNodes => InfoNodes;
|
||||
private readonly IWorldNodeService _worldNodeService;
|
||||
private static readonly ConcurrentDictionary<int, WorldInfoNode> InfoNodes = new();
|
||||
|
||||
public WorldInfoNodeService(IWorldNodeService worldNodeService)
|
||||
{
|
||||
_worldNodeService = worldNodeService;
|
||||
|
||||
_worldNodeService.OnNodeRegistered += OnNodeRegistered;
|
||||
}
|
||||
|
||||
private void OnNodeRegistered(IWorldNode obj)
|
||||
{
|
||||
if (obj is not WorldInfoNode infoNode) return;
|
||||
InfoNodes.TryAdd(obj.Id, infoNode);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
InfoNodes.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,13 +5,8 @@ using System.Collections.Generic;
|
||||
namespace BITKit.WorldNode
|
||||
{
|
||||
[Serializable]
|
||||
public struct WorldInfoNpcStartNode:IWorldNode
|
||||
public class WorldInfoNpcStartNode:IWorldNode
|
||||
{
|
||||
public string Address;
|
||||
public int Id { get; set; }
|
||||
public object WorldObject { get; set; }
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,14 +5,8 @@ using System.Collections.Generic;
|
||||
namespace BITKit.WorldNode
|
||||
{
|
||||
[Serializable]
|
||||
public struct WorldInfoPlayerStart:IWorldNode
|
||||
public class WorldInfoPlayerStart:IWorldNode
|
||||
{
|
||||
public static WorldInfoPlayerStart Current { get; set; }
|
||||
public int Id { get; set; }
|
||||
public object WorldObject { get; set; }
|
||||
public void Initialize()
|
||||
{
|
||||
Current = this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -19,8 +19,6 @@ namespace BITKit.WorldNode
|
||||
public IReference MapName;
|
||||
public float3 Position;
|
||||
public float3 EulerAngle;
|
||||
public int Id { get; set; }
|
||||
public object WorldObject { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user