This commit is contained in:
CortexCore
2025-03-03 18:44:00 +08:00
parent 561974a1ea
commit c29bf0d12f
84 changed files with 3986 additions and 1027 deletions

View File

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

View File

@@ -13,35 +13,44 @@ 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();
public EntitiesService(ILogger<EntitiesService> logger, IFixedTicker ticker)
{
_count++;
_logger = logger;
_ticker = ticker;
_ticker.Add(OnTick);
}
private void OnTick(float obj)
{
while (OnAddQueue.TryDequeue(out var entity))
{
OnAdd?.Invoke(entity);
}
}
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;
}
public bool UnRegister(IEntity entity)
{
if (!Entities.TryRemove(entity.Id, out _)) return false;
OnRemove?.Invoke(entity);
OnRemove?.Invoke(entity);
return true;
}
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
@@ -262,6 +271,8 @@ namespace BITKit.Entities
_logger.LogInformation($"已释放,还剩{_count}个实例");
_cancellationTokenSource?.Dispose();
_ticker.Remove(OnTick);
}
}
}

View File

@@ -17,8 +17,7 @@ namespace BITKit.Entities
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;
@@ -39,7 +38,6 @@ namespace BITKit.Entities
public void Dispose()
{
_cancellationTokenSource.Cancel();
_serviceProvider.Dispose();
}
}

View File

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

View 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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: ca82e09109a03de47b638f35b49b59e5
guid: 230e015069b45484f9c85d1aba1e901c
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

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

View File

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

View File

@@ -8,12 +8,6 @@ namespace BITKit.WorldNode
/// </summary>
public interface IWorldNode
{
public int Id { get; set; }
public object WorldObject { get; set; }
public void Initialize()
{
}
}
}

View File

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

View File

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

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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 132a27b99db1e664692937ec23f40676
guid: 5e1cbe087263f1342abbc3c6fb5c4068
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

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

View File

@@ -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()
{
}
}
}

View File

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

View File

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

View File

@@ -34,21 +34,47 @@ namespace BITKit.IO
var scriptableObject = objs[index];
var entity = new Entity();
var idComponent = new IdComponent();
var idComponent = new IdComponent()
{
Name = scriptableObject.name
};
entity.ServiceCollection.AddSingleton(idComponent);
var type = scriptableObject.GetType();
var typeName = type.Name;
entity.ServiceCollection.AddSingleton(type, scriptableObject);
foreach (var x in type.GetInterfaces())
{
entity.ServiceCollection.AddSingleton(x, scriptableObject);
}
while (type is not null)
{
if (type.BaseType is null) break;
entity.ServiceCollection.AddSingleton(type.BaseType, scriptableObject);
type = type.BaseType;
typeName += $":{type.Name}";
}
_entitiesService.Register(entity);
logger?.LogInformation($"已加载:{scriptableObject.name}:{type.Name},剩余:{index + 1}/{objs.Count}");
logger?.LogInformation($"已加载:{scriptableObject.name}:{typeName},剩余:{index + 1}/{objs.Count}");
_registeredEntities.Add(entity);
continue;
}
logger?.LogInformation("加载完成");
return;
}
public void Dispose()

View File

@@ -74,7 +74,7 @@ namespace BITKit.UX
foreach (var fieldInfo in self.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)
.Where(x => x.GetCustomAttribute<UXBindPathAttribute>() is not null)
)
{

View File

@@ -22,6 +22,7 @@ namespace BITKit.GameEditor
protected ListView listView { get; private set; }
protected VisualElement container { get; private set; }
protected VisualElement _actionContainer { get; private set; }
protected TextField _newNameField { get; private set; }
private Button _createButton;
protected virtual void OnEnable()
@@ -94,11 +95,37 @@ namespace BITKit.GameEditor
{
var newNameContainer = scroll.Create<GroupBox>();
newNameContainer.style.flexDirection = FlexDirection.Row;
var newNameField = newNameContainer.Create<TextField>();
var newNameField =_newNameField = newNameContainer.Create<TextField>();
newNameField.style.flexGrow = 1;
var confirmButton = newNameContainer.Create<Button>();
confirmButton.text = "重命名";
confirmButton.clicked += () =>
{
var newName = newNameField.value;
if (string.IsNullOrEmpty(newName))
{
EditorUtility.DisplayDialog("警告", "文件名不能为空", "OK");
return;
}
var selected = listView.selectedItem as Object;
if(!selected)return;
var path = AssetDatabase.GetAssetPath(selected);
if (EditorUtility.DisplayDialog("询问", $"确定要重命名{Path.GetFileName(path)}为{newName}", "确定", "取消") is false)
{
return;
}
AssetDatabase.RenameAsset(path, newName);
RebuildList();
};
}
@@ -148,9 +175,12 @@ namespace BITKit.GameEditor
{
var selected = obj.FirstOrDefault() as Object;
var serializedObject = new SerializedObject(selected);
BITInspectorExtensions.FillDefaultInspector(container,serializedObject, true);
BITInspectorExtensions.FillDefaultInspector(container, serializedObject, true);
container.Bind(serializedObject);
if (selected)
_newNameField.SetValueWithoutNotify(selected.name);
}
protected virtual VisualElement MakeItem()
{
var container = new VisualElement();
@@ -187,6 +217,8 @@ namespace BITKit.GameEditor
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
RebuildList();
Debug.Log($"复制成功:{path}");
}
}