This commit is contained in:
CortexCore 2025-02-24 23:02:43 +08:00
parent 41715e4413
commit 8261a458e2
104 changed files with 2924 additions and 686 deletions

View File

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6d0be10870bf27f4b8b1c97ed8809308
guid: 66cea81a8a712d348aded0734f12d2ef
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -2,13 +2,11 @@ using System.Dynamic;
namespace BITKit.Entities
{
public interface IdComponent
public class IdComponent
{
ulong Id { get; set; }
}
public interface IdComponent_String
{
string DbId { get; }
public int Id { get; set; }
public string Name { get; set; }
public string Group { get; set; }
}
}

View File

@ -0,0 +1,10 @@
using System.Collections;
using System.Collections.Generic;
namespace BITKit.Entities
{
public class LocalPlayerComponent
{
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b030d336e53d2c646a7b382eb83897bf
guid: 46172bbdf3a15d4458646b3e80b6702c
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -5,28 +5,42 @@ using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.AccessControl;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace BITKit.Entities
{
public class EntitiesService:IEntitiesService
public class EntitiesService:IEntitiesService,IDisposable
{
private readonly ConcurrentDictionary<int, IEntity> _entities = new();
private readonly ConcurrentDictionary<Type, IEntity[]> _queryCache = new();
private readonly ConcurrentDictionary<(Type,Type), IEntity[]> _query2ComponentsCache = new();
private readonly ConcurrentDictionary<(Type,Type,Type), IEntity[]> _query3ComponentsCache = new();
private readonly ILogger<EntitiesService> _logger;
private static int _count;
public EntitiesService()
{
_count++;
}
public EntitiesService(ILogger<EntitiesService> logger)
{
_count++;
_logger = logger;
}
private static readonly ConcurrentDictionary<int, IEntity> Entities = new();
public event Action<IEntity> OnAdd;
public event Action<IEntity> OnRemove;
public IEntity[] Entities => _entities.Values.ToArray();
IEntity[] IEntitiesService.Entities => Entities.Values.ToArray();
public bool Register(IEntity entity)
{
if (!_entities.TryAdd(entity.Id, entity)) return false;
if (!Entities.TryAdd(entity.Id, entity)) return false;
OnAdd?.Invoke(entity);
return true;
}
public bool UnRegister(IEntity entity)
{
if (!_entities.TryRemove(entity.Id, out _)) return false;
if (!Entities.TryRemove(entity.Id, out _)) return false;
OnRemove?.Invoke(entity);
return true;
}
@ -34,17 +48,17 @@ namespace BITKit.Entities
private readonly CancellationTokenSource _cancellationTokenSource = new();
public IEntity Get(int id)
{
return _entities[id];
return Entities[id];
}
public bool TryGetEntity(int id, out IEntity entity)
{
return _entities.TryGetValue(id, out entity);
return Entities.TryGetValue(id, out entity);
}
public IEntity GetOrAdd(int id, Func<int, IEntity> factory)
{
return _entities.GetOrAdd(id, factory);
return Entities.GetOrAdd(id, factory);
}
public IEntity[] Query<T>()
@ -54,16 +68,36 @@ namespace BITKit.Entities
public T[] QueryComponents<T>()
{
throw new NotImplementedException();
var list = new List<T>();
foreach (var entity in Entities.Values)
{
if (entity.ServiceProvider.GetService<T>() is { } t1)
{
list.Add(t1);
}
}
return list.ToArray();
// return _queryCache.GetOrAdd(typeof(T), type =>
// {
// return _entities.Values.Where(entity => entity.TryGetComponent(out T component)).ToArray();
// }).Cast<T>().ToArray();
}
public (T, T1)[] QueryComponents<T, T1>()
{
throw new NotImplementedException();
// List<(T, T1)> list = new();
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))
@ -73,7 +107,18 @@ namespace BITKit.Entities
}
public (T, T1, T2)[] QueryComponents<T, T1, T2>()
{
throw new NotImplementedException();
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)
// {
@ -85,7 +130,19 @@ namespace BITKit.Entities
public (T, T1, T2, T3)[] QueryComponents<T, T1, T2, T3>()
{
throw new NotImplementedException();
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)
// {
@ -97,7 +154,20 @@ namespace BITKit.Entities
public (T, T1, T2, T3, T4)[] QueryComponents<T, T1, T2, T3, T4>()
{
throw new NotImplementedException();
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)
// {
@ -109,7 +179,21 @@ namespace BITKit.Entities
public (T, T1, T2, T3, T4, T5)[] QueryComponents<T, T1, T2, T3, T4, T5>()
{
throw new NotImplementedException();
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)
// {
@ -121,7 +205,22 @@ namespace BITKit.Entities
public (T, T1, T2, T3, T4, T5, T6)[] QueryComponents<T, T1, T2, T3, T4, T5, T6>()
{
throw new NotImplementedException();
var list = new List<(T, T1, T2, T3, T4, T5, T6)>();
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 &&
entity.ServiceProvider.GetService<T6>() is { } t7)
{
list.Add((t1, t2, t3, t4, t5, t6, t7));
}
}
return list.ToArray();
// List<(T, T1, T2, T3, T4, T5, T6)> list = new();
// foreach (var entity in _entities.Values)
// {
@ -131,10 +230,38 @@ namespace BITKit.Entities
// return list.ToArray();
}
public ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>[] QueryComponents<T, T1, T2, T3, T4, T5, T6, TRest>() where TRest : struct
public ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>[] QueryComponents<T, T1, T2, T3, T4, T5, T6, TRest>()
where TRest : struct
{
throw new NotImplementedException();
// throw new NotImplementedException();
var list = new List<ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>>();
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 &&
entity.ServiceProvider.GetService<T6>() is { } t7 &&
entity.ServiceProvider.GetService<TRest>() is { } t8)
{
list.Add(new ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>(t1, t2, t3, t4, t5, t6, t7, t8));
}
}
return list.ToArray();
}
public void Dispose()
{
_count--;
if (_count <= 0)
{
Entities.Clear();
}
_logger.LogInformation($"已释放,还剩{_count}个实例");
_cancellationTokenSource?.Dispose();
}
}
}

View File

@ -24,7 +24,6 @@ namespace BITKit.Entities
/// </summary>
public interface IEntitiesService
{
/// <summary>
/// 当添加Entity时
/// </summary>

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 2e822c111559c204ba6fc483b283d193
guid: ad41977c90d905e4fa0efdcfd3260ccc
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -0,0 +1,14 @@
{
"name": "BITKit.Hotkey",
"rootNamespace": "",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 01cf845b40a364e4cbaee058936fa4a3
guid: 3abaaefa7af558d44ba20cea1c43d602
AssemblyDefinitionImporter:
externalObjects: {}
userData:

View File

@ -0,0 +1,47 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BITKit.UX.Hotkey
{
public interface IHotkeyProvider
{
string Name { get; }
string Description { get; }
object Data { get; }
bool Enabled { get; }
Action OnPerform { get; }
float HoldDuration => 0;
}
public struct HotkeyProvider : IHotkeyProvider
{
public string Name { get; set; }
public string Description { get; set; }
public object Data { get; set; }
public bool Enabled { get; set; }
public Action OnPerform { get; set; }
public float HoldDuration { get; set; }
}
public interface IHotkeyCollection
{
IEnumerable<IHotkeyProvider> Hotkeys { get; }
void Register(IHotkeyProvider hotkey);
void UnRegister(IHotkeyProvider hotkey);
}
public class HotkeyCollection:IHotkeyCollection
{
private readonly HashSet<IHotkeyProvider> _hotkeys = new();
public IEnumerable<IHotkeyProvider> Hotkeys => _hotkeys;
public void Register(IHotkeyProvider hotkey)
{
_hotkeys.Add(hotkey);
}
public void UnRegister(IHotkeyProvider hotkey)
{
_hotkeys.Remove(hotkey);
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 7ba6497a10fb8e14dab1acb39b2ec8aa
guid: 0ad36bc56dd5406418bf8ea67f4bc010
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -53,7 +53,13 @@ namespace BITKit
public interface IScriptableItemProperty
{
}
/// <summary>
/// 控制器(分)类
/// </summary>
public interface IScriptableControllerClass
{
}
/// <summary>
/// 基础物品
/// </summary>
@ -88,6 +94,11 @@ namespace BITKit
/// 属性
/// </summary>
IReadOnlyDictionary<Type, IScriptableItemProperty> Properties { get; }
/// <summary>
/// 控制器类
/// </summary>
IScriptableControllerClass ControllerClass { get; }
/// <summary>
/// 价值

View File

@ -14,6 +14,7 @@ using BITKit.IO;
using BITKit.UX;
using Cysharp.Threading.Tasks;
using Microsoft.CSharp;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace BITKit.Mod
@ -133,17 +134,26 @@ namespace BITKit.Mod
var list=new List<ModPackage>();
var path = Path.Combine(Environment.CurrentDirectory, "Mods");
var dir = new DirectoryInfo(path);
dir.Create();
foreach (var x in dir.GetDirectories())
try
{
var file = Path.Combine(x.FullName,ModPackage.DefaultFileName);
if(File.Exists(file) is false)continue;
var package = JsonConvert.DeserializeObject<ModPackage>(await File.ReadAllTextAsync(file))!;
package.PackagePath = file;
package.WorkDirectory = x.FullName;
list.Add(package);
var dir = new DirectoryInfo(path);
dir.Create();
foreach (var x in dir.GetDirectories())
{
var file = Path.Combine(x.FullName,ModPackage.DefaultFileName);
if(File.Exists(file) is false)continue;
var package = JsonConvert.DeserializeObject<ModPackage>(await File.ReadAllTextAsync(file))!;
package.PackagePath = file;
package.WorkDirectory = x.FullName;
list.Add(package);
}
}
catch (Exception e)
{
BIT4Log.LogException(e);
}
return list.ToArray();
}
@ -200,19 +210,16 @@ namespace BITKit.Mod
private static CancellationTokenSource _CancellationTokenSource;
private static readonly ConcurrentDictionary<string,IMod> _InstalledMods=new();
public static async UniTask Initialize()
public static async UniTask Initialize(ILogger logger=null)
{
BIT4Log.Log<ModService>("Mod服务已启动");
logger?.LogInformation("Mod服务已启动");
_CancellationTokenSource = new CancellationTokenSource();
try
{
try
{
var modPath = Path.Combine(Environment.CurrentDirectory, "Mods\\");
var modPath = Path.Combine(Environment.CurrentDirectory, @"Mods\");
PathHelper.EnsureDirectoryCreated(modPath);
var directoryInfo = new DirectoryInfo(modPath);
foreach (var fileInfo in directoryInfo.GetFiles())
@ -238,18 +245,16 @@ namespace BITKit.Mod
}
catch (Exception e)
{
BIT4Log.Warning<ModService>("自动加载Mod失败");
logger?.LogWarning("自动加载Mod失败");
BIT4Log.LogException(e);
}
}
catch (Exception e)
{
logger?.LogWarning("Mod服务遇到了错误,已停止");
BIT4Log.LogException(e);
BIT4Log.Warning<ModService>("Mod服务遇到了错误,已停止");
}
}
public static void Dispose()
@ -336,7 +341,7 @@ namespace BITKit.Mod
}
var fileInfo = new FileInfo(path);
var fileInfo = new FileInfo(path);
switch (fileInfo.Extension)
{
case ".dll":
@ -360,6 +365,10 @@ namespace BITKit.Mod
}
public static async UniTask Load(IMod mod)
{
await IsBusy;
using var _ = IsBusy.GetHandle();
mod.OnInitialize();
OnModLoad?.Invoke(mod);
BIT4Log.Log<ModService>($"加载Mod:{mod.GetType().FullName}");
@ -381,7 +390,7 @@ namespace BITKit.Mod
BIT4Log.Log<ModService>($"卸载Mod:{mod.GetType().FullName}");
}
public static async void Install(IMod mod)
public static async UniTask Install(IMod mod)
{
await IsBusy;
@ -404,11 +413,12 @@ namespace BITKit.Mod
mod.OnInitialized();
OnModLoaded?.Invoke(mod);
}
public static void UnInstall(IMod mod)
public static async UniTask UnInstall(IMod mod)
{
await IsBusy;
using var _ = IsBusy.GetHandle();
if(_InstalledMods.ContainsKey(mod.PackageName) is false) return;
_InstalledMods.TryRemove(mod.PackageName);
Mods = _InstalledMods.Values.ToArray();

View File

@ -8,7 +8,20 @@ namespace BITKit.Mod
public partial class ModService
{
public static Func<string, UniTask<object>> LoadAssetAsyncFactory;
public static Func<string[], UniTask<IReadOnlyList<object>>> LoadAssetsAsyncFactory;
public static async UniTask<IReadOnlyList<T>> LoadAssets<T>(params string[] tags)
{
var list = new List<T>();
foreach (var func in LoadAssetsAsyncFactory.CastAsFunc())
{
foreach (var obj in await func.Invoke(tags))
{
list.Add((T)obj);
}
}
return list;
}
public static async UniTask<T> LoadAsset<T>(string location) where T : class
{
if (LoadAssetAsyncFactory is null)

View File

@ -272,6 +272,14 @@ namespace BITKit
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
foreach (var parameterInfo in methodInfo.GetParameters())
{
if (parameterInfo.IsOut)
{
codeBuilder.AppendLine($"{parameterInfo.Name} = default;");
}
}
codeBuilder.AppendLine(" using var ms = new MemoryStream();");
codeBuilder.AppendLine(" using var writer = new BinaryWriter(ms);");
codeBuilder.AppendLine(" writer.Write((byte)NetCommandType.Rpc);");
@ -466,6 +474,7 @@ namespace BITKit
{
options= options.AddReferences(referencedAssembly);
}
options = options.AddReferences(typeof(BITApp).Assembly);
var assembly = BITSharp.Compile(code, options);

View File

@ -110,6 +110,11 @@ namespace BITKit
{
codeBuilder.AppendLine(GenerateMethod(method));
}
foreach (var eventInfo in type.GetEvents())
{
codeBuilder.AppendLine(GenerateEvent(eventInfo));
}
codeBuilder.AppendLine(AfterGenerate(type));
@ -136,25 +141,32 @@ namespace BITKit
public virtual string GenerateMethod(MethodInfo methodInfo)
{
var codeBuilder = new StringBuilder();
HashSet<MethodInfo> propertyMethods = new();
HashSet<MethodInfo> ignoreMethods = new();
foreach (var eventInfo in methodInfo.DeclaringType!.GetEvents())
{
ignoreMethods.Add(eventInfo.AddMethod);
ignoreMethods.Add(eventInfo.RemoveMethod);
}
foreach (var propertyInfo in methodInfo.DeclaringType!.GetProperties())
{
if (propertyInfo.GetMethod is not null)
{
propertyMethods.Add(propertyInfo.GetMethod);
ignoreMethods.Add(propertyInfo.GetMethod);
}
if (propertyInfo.SetMethod is not null)
{
propertyMethods.Add(propertyInfo.SetMethod);
ignoreMethods.Add(propertyInfo.SetMethod);
}
}
if(propertyMethods.Contains(methodInfo))return string.Empty;
if(ignoreMethods.Contains(methodInfo))return string.Empty;
string methodName = methodInfo.Name;
string parameters = string.Join(", ", methodInfo.GetParameters()
.Select(p => $"{CSharpName(p.ParameterType)} {p.Name}"));
.Select(p => $"{(p.IsOut?"out":string.Empty)} {CSharpName(p.IsOut?p.ParameterType.GetElementType():p.ParameterType)} {p.Name}"));
var isAwaitable = methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
@ -194,7 +206,17 @@ namespace BITKit
public virtual string GenerateMethodContext(MethodInfo methodInfo)
{
return methodInfo.ReturnType == typeof(void) ? string.Empty : $"return default;";
var codeBuilder = new StringBuilder();
foreach (var parameterInfo in methodInfo.GetParameters())
{
if (parameterInfo.IsOut)
{
codeBuilder.AppendLine($"{parameterInfo.Name} = default;");
}
}
codeBuilder.AppendLine(methodInfo.ReturnType == typeof(void) ? string.Empty : $"return default;");
return codeBuilder.ToString();
}
public virtual string GenerateField(FieldInfo fieldInfo)
@ -209,7 +231,7 @@ namespace BITKit
public virtual string GenerateEvent(EventInfo eventInfo)
{
throw new NotImplementedException();
return $"public event {CSharpName(eventInfo.EventHandlerType)} {eventInfo.Name};";
}
}
#if UNITY_5_3_OR_NEWER
@ -257,7 +279,6 @@ namespace BITKit
{
result.Add(fieldInfo.FieldType.Assembly);
}
foreach (var propertyInfo in type.GetProperties())
{
result.Add(propertyInfo.PropertyType.Assembly);
@ -269,6 +290,10 @@ namespace BITKit
{
result.Add(argument.Assembly);
}
foreach (var parameterInfo in methodInfo.GetParameters())
{
result.Add(parameterInfo.ParameterType.Assembly);
}
}
return result;
}
@ -295,7 +320,24 @@ namespace BITKit
using var ms = new MemoryStream();
var result = script.GetCompilation().Emit(ms);
if (!result.Success) throw new Exception(string.Join("\n",result.Diagnostics));
if (!result.Success)
{
var report = new StringBuilder();
report.AppendLine(code);
foreach (var reference in script.GetCompilation().References)
{
report.AppendLine("Assembly referenced:" + reference.Display);
}
foreach (var diagnostic in result.Diagnostics)
{
report.AppendLine(diagnostic.ToString());
}
throw new Exception(report.ToString());
}
ms.Seek(0, SeekOrigin.Begin);
var assembly = Assembly.Load(ms.ToArray()); // 加载程序集

View File

@ -25,8 +25,8 @@ namespace BITKit.StateMachine
public bool Enabled { get; set; } = true;
public T CurrentState { get;private set; }
public T NextOrCurrentState => _nextTargetState.IfNotAllow(CurrentState);
private T _nextState;
public event Action<T, T> OnStateChanging;
public event Func<T, T, UniTask> OnStateChangeAsync;
public event Action<T, T> OnStateChanged;
public event Action<T> OnStateRegistered;
public IReadOnlyDictionary<int, T> Dictionary => _dictionary;
@ -53,21 +53,18 @@ namespace BITKit.StateMachine
{
if (CurrentState is null) return;
using var _ = IsBusy.GetHandle();
await CurrentState.OnStateUpdateAsync(deltaTime);
CurrentState.OnStateUpdate(deltaTime);
await CurrentState.OnStateUpdateAsync(deltaTime);
}
public async void DisposeState()
{
await IsBusy;
if(_cancellationTokenSource.IsCancellationRequested)return;
if (_cancellationTokenSource.IsCancellationRequested) return;
if (CurrentState is null) return;
using var _ = IsBusy.GetHandle();
if (CurrentState is not null)
{
CurrentState.Enabled = false;
}
CurrentState.Enabled = false;
await CurrentState.OnStateExitAsync(CurrentState, null);
CurrentState.OnStateExit(CurrentState, null);
CurrentState = null;
@ -108,10 +105,15 @@ namespace BITKit.StateMachine
}
_nextTargetState.SetValueThenAllow(nextState);
await IsBusy;
if(CurrentState==nextState)return;
if(_cancellationTokenSource.IsCancellationRequested)return;
using var _ = IsBusy.GetHandle();
OnStateChanging?.Invoke(CurrentState,nextState);
if (_dictionary.TryAdd(nextState.Identifier, nextState))
await OnStateChangeAsync.UniTaskFunc(CurrentState,nextState);
if (nextState is not null && _dictionary.TryAdd(nextState.Identifier, nextState))
{
await nextState.InitializeAsync();
if(_cancellationTokenSource.IsCancellationRequested)return;
@ -125,6 +127,11 @@ namespace BITKit.StateMachine
CurrentState.OnStateExit(CurrentState,nextState);
}
if (_nextTargetState.Allow && _nextTargetState.Value != nextState)
{
return;
}
var tempState = CurrentState;
CurrentState = _nextTargetState.Value;
_nextTargetState.Clear();

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
@ -44,11 +45,16 @@ namespace BITKit.StateMachine
UniTask OnStateExitAsync(IState old, IState newState);
}
public interface IStateMachine<T> where T:IState
public interface IMicroStateMachine<T>
{
T CurrentState { get; }
event Action<T, T> OnStateChanged;
T TransitionState<TState>() where TState : T;
T TransitionState(T state);
}
public interface IStateMachine<T>:IMicroStateMachine<T> where T:IState
{
bool Enabled { get; set; }
T CurrentState { get; }
event Action<T,T> OnStateChanged;
event Action<T> OnStateRegistered;
event Action<T> OnStateUnRegistered;
@ -56,13 +62,72 @@ namespace BITKit.StateMachine
void Initialize();
void UpdateState(float deltaTime);
void DisposeState();
T TransitionState<TState>() where TState : T;
T TransitionState(T state);
void Register(T newState) => throw new NotImplementedException("未实现的接口");
void UnRegister(T newState) => throw new NotImplementedException("未实现的接口");
void InvokeOnStateRegistered(T state){}
void InvokeOnStateUnRegistered(T state){}
}
public class MicroStateMachine<T> : IMicroStateMachine<T>
{
public T CurrentState { get; private set; }
public event Action<T, T> OnStateChanged;
private readonly ConcurrentDictionary<Type,T> _stateDictionary=new();
public T TransitionState<TState>() where TState : T
{
var state = _stateDictionary.GetOrAdd(typeof(TState), Activator.CreateInstance<TState>());
return TransitionState(state);
}
public T TransitionState(T state)
{
_stateDictionary.TryAdd(state.GetType(), state);
if (Equals(state, CurrentState)) return default;
var currentState = CurrentState;
CurrentState = state;
OnStateChanged?.Invoke(currentState, state);
return state;
}
}
public abstract class StateAsync:IStateAsync
{
public virtual bool Enabled { get; set; }
public virtual void Initialize()
{
}
public virtual void OnStateEntry(IState old)
{
}
public virtual void OnStateUpdate(float deltaTime)
{
}
public virtual void OnStateExit(IState old, IState newState)
{
}
public virtual int Identifier { get; set; }
public virtual UniTask InitializeAsync()
{
return UniTask.CompletedTask;
}
public virtual UniTask OnStateEntryAsync(IState old)
{ return UniTask.CompletedTask;
}
public virtual UniTask OnStateUpdateAsync(float deltaTime)
{ return UniTask.CompletedTask;
}
public virtual UniTask OnStateExitAsync(IState old, IState newState)
{ return UniTask.CompletedTask;
}
}
public static class StateMachineUtils
{
public static void Register<T>(this IStateMachine<T> stateMachine, T newState) where T : IState

View File

@ -30,6 +30,7 @@ namespace BITKit.UX
/// 该面板是否启用玩家输入
/// </summary>
bool AllowInput { get; }
object Root { get; }
/// <summary>
/// 事件回调,当面板被打开时触发
/// </summary>
@ -43,6 +44,8 @@ namespace BITKit.UX
event Func<UniTask> OnExitAsync;
event Action OnExitCompleted;
public event Action OnInitiated;
public event Func<UniTask> OnInitiatedAsync;
void OnTick(float deltaTime);
}
}

View File

@ -1,5 +1,6 @@
using System;
using Cysharp.Threading.Tasks;
using Unity.Mathematics;
namespace BITKit.UX
{
@ -52,6 +53,13 @@ namespace BITKit.UX
/// 面板改变回调
/// </summary>
public event Action<IUXPanel, IUXPanel> OnPanelChanged;
/// <summary>
/// 获取可交互的元素
/// </summary>
/// <param name="position"></param>
/// <param name="obj"></param>
/// <returns></returns>
public bool TryPick(float2 position, out object obj);
}
}

View File

@ -10,7 +10,7 @@ namespace BITKit.WorldNode
/// </summary>
public interface IWorldNodeService
{
public IReadOnlyDictionary<int, IWorldNode> WorldNodes { get; }
public IReadOnlyDictionary<int, HashSet<IWorldNode>> WorldNodes { get; }
public void RegisterNode(IWorldNode node);
public event Action<IWorldNode> OnNodeRegistered;
}
@ -21,12 +21,12 @@ namespace BITKit.WorldNode
public class WorldNodeService : IWorldNodeService,IDisposable
{
public static event Action<IWorldNode> OnNodeRegistered;
IReadOnlyDictionary<int, IWorldNode> IWorldNodeService.WorldNodes => WorldNodes;
private static readonly ConcurrentDictionary<int, IWorldNode> WorldNodes = new();
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.TryAdd(node.Id, node);
WorldNodes.GetOrCreate(node.Id).Add(node);
}
event Action<IWorldNode> IWorldNodeService.OnNodeRegistered
{

View File

@ -1,3 +1,4 @@
#if UNITY_5_3_OR_NEWER
using System;
using System.Collections;
using System.Collections.Generic;
@ -12,6 +13,8 @@ namespace BITKit.WorldNode
[SerializeReference, SubclassSelector] private IWorldNode worldNode;
public int Id { get; set; }
public IWorldNode WorldNode => worldNode;
public object WorldObject
{
get => gameObject;
@ -27,10 +30,10 @@ namespace BITKit.WorldNode
Id = gameObject.GetInstanceID();
worldNode.Id = Id;
worldNode.WorldObject = gameObject;
worldNodeService.RegisterNode(worldNode);
worldNode.Initialize();
worldNodeService.RegisterNode(worldNode);
Destroy(this);
}
}
}
#endif

View File

@ -20,17 +20,24 @@ namespace BITKit.WorldNode
[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 => name?.Value;
public string Description => description?.Value;
public string Name { get; set; }
public string Description { get; set; }
}
public sealed class WorldInfoNodeService : IDisposable
{
public IReadOnlyDictionary<int, WorldInfoNode> WorldInfoNodes => _infoNodes;
public IReadOnlyDictionary<int, WorldInfoNode> WorldInfoNodes => InfoNodes;
private readonly IWorldNodeService _worldNodeService;
private readonly ConcurrentDictionary<int, WorldInfoNode> _infoNodes = new();
private static readonly ConcurrentDictionary<int, WorldInfoNode> InfoNodes = new();
public WorldInfoNodeService(IWorldNodeService worldNodeService)
{
@ -42,12 +49,12 @@ namespace BITKit.WorldNode
private void OnNodeRegistered(IWorldNode obj)
{
if (obj is not WorldInfoNode infoNode) return;
_infoNodes.TryAdd(obj.Id, infoNode);
InfoNodes.TryAdd(obj.Id, infoNode);
}
public void Dispose()
{
_infoNodes.Clear();
InfoNodes.Clear();
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BITKit.WorldNode
{
[Serializable]
public struct WorldInfoNpcStartNode:IWorldNode
{
public string Address;
public int Id { get; set; }
public object WorldObject { get; set; }
public void Initialize()
{
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: c9d7136f598a780459b866ee63545a5f
guid: f2fba06a66e155a4580df30630f030fc
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -14,6 +14,22 @@ namespace BITKit
}
public class ValueWrapper<T> : IWrapper<T>
{
public ValueWrapper()
{
var type = typeof(T);
if (type == typeof(string))
{
_value = string.Empty.As<T>();
return;
}
if(type.IsAbstract || type.IsInterface)return;
// 检查类型是否具有公共无参数构造函数
var constructor = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null);
if (constructor is not null)
{
_value = Activator.CreateInstance<T>();
}
}
public Action<T, T> OnValueChanged { get; set; }
public T Value
@ -27,7 +43,7 @@ namespace BITKit
}
}
private T _value = typeof(T) == typeof(string) ? string.Empty.As<T>() : Activator.CreateInstance<T>();
private T _value;
public object Obj
{
get => Value;

View File

@ -1,6 +1,8 @@
using System;
using Newtonsoft.Json;
using Unity.Mathematics;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
#if NET5_0_OR_GREATER
namespace BITKit
@ -190,4 +192,4 @@ namespace BITKit
}
}
}
#endif
#endif

View File

@ -0,0 +1,3 @@
{
"reference": "GUID:14fe60d984bf9f84eac55c6ea033a8f4"
}

View File

@ -1,7 +1,6 @@
fileFormatVersion: 2
guid: 2da90b0890b24e644a1900960dc2fb9c
folderAsset: yes
DefaultImporter:
guid: 28ed10548f6aa6944ab6ac01ce4f9faf
AssemblyDefinitionReferenceImporter:
externalObjects: {}
userData:
assetBundleName:

View File

@ -0,0 +1,109 @@
using System;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using Unity.Mathematics;
namespace BITKit
{
public class UnityMathematicsYamlConverters : IYamlTypeConverter
{
public bool Accepts(Type type)
{
return type == typeof(int2) || type == typeof(int3) || type == typeof(int4) ||
type == typeof(float2) || type == typeof(float3) || type == typeof(float4) ||
type == typeof(quaternion);
}
public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
var scalar = parser.Consume<Scalar>();
var parts = scalar.Value.Split(',');
if (type == typeof(int2))
{
if (parts.Length != 2) throw new YamlException(scalar.Start, scalar.End, "Expected 2 int values for int2.");
return new int2(int.Parse(parts[0]), int.Parse(parts[1]));
}
else if (type == typeof(int3))
{
if (parts.Length != 3) throw new YamlException(scalar.Start, scalar.End, "Expected 3 int values for int3.");
return new int3(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]));
}
else if (type == typeof(int4))
{
if (parts.Length != 4) throw new YamlException(scalar.Start, scalar.End, "Expected 4 int values for int4.");
return new int4(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
}
else if (type == typeof(float2))
{
if (parts.Length != 2) throw new YamlException(scalar.Start, scalar.End, "Expected 2 float values for float2.");
return new float2(float.Parse(parts[0]), float.Parse(parts[1]));
}
else if (type == typeof(float3))
{
if (parts.Length != 3) throw new YamlException(scalar.Start, scalar.End, "Expected 3 float values for float3.");
return new float3(float.Parse(parts[0]), float.Parse(parts[1]), float.Parse(parts[2]));
}
else if (type == typeof(float4))
{
if (parts.Length != 4) throw new YamlException(scalar.Start, scalar.End, "Expected 4 float values for float4.");
return new float4(float.Parse(parts[0]), float.Parse(parts[1]), float.Parse(parts[2]), float.Parse(parts[3]));
}
else if (type == typeof(quaternion))
{
if (parts.Length != 4) throw new YamlException(scalar.Start, scalar.End, "Expected 4 float values for quaternion.");
return new quaternion(float.Parse(parts[0]), float.Parse(parts[1]), float.Parse(parts[2]), float.Parse(parts[3]));
}
throw new YamlException(scalar.Start, scalar.End, $"Unsupported type: {type}");
}
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
{
string scalarValue = "";
if (type == typeof(int2))
{
var int2Value = (int2)value;
scalarValue = $"{int2Value.x},{int2Value.y}";
}
else if (type == typeof(int3))
{
var int3Value = (int3)value;
scalarValue = $"{int3Value.x},{int3Value.y},{int3Value.z}";
}
else if (type == typeof(int4))
{
var int4Value = (int4)value;
scalarValue = $"{int4Value.x},{int4Value.y},{int4Value.z},{int4Value.w}";
}
else if (type == typeof(float2))
{
var float2Value = (float2)value;
scalarValue = $"{float2Value.x},{float2Value.y}";
}
else if (type == typeof(float3))
{
var float3Value = (float3)value;
scalarValue = $"{float3Value.x},{float3Value.y},{float3Value.z}";
}
else if (type == typeof(float4))
{
var float4Value = (float4)value;
scalarValue = $"{float4Value.x},{float4Value.y},{float4Value.z},{float4Value.w}";
}
else if (type == typeof(quaternion))
{
var quaternionValue = (quaternion)value;
scalarValue = $"{quaternionValue.value.x},{quaternionValue.value.y},{quaternionValue.value.z},{quaternionValue.value.w}";
}
else
{
throw new YamlException($"Unsupported type: {type}");
}
emitter.Emit(new Scalar(scalarValue));
}
}
}

View File

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

View File

@ -0,0 +1,3 @@
{
"reference": "GUID:14fe60d984bf9f84eac55c6ea033a8f4"
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d31ceb431a775db4a8757a1208d82106
AssemblyDefinitionReferenceImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -36,7 +36,7 @@
"name": "Crouch",
"type": "Button",
"id": "eab8ff36-98a9-4a2d-8393-167d9347227b",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Tap",
"initialStateCheck": false
@ -45,7 +45,7 @@
"name": "Crawl",
"type": "Button",
"id": "350ae177-bf96-40b1-aff0-e10865947df9",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@ -54,7 +54,7 @@
"name": "HoldCrouch",
"type": "Button",
"id": "07dfe885-d709-4482-9686-57f671b1aed6",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press",
"initialStateCheck": false
@ -63,7 +63,7 @@
"name": "Fire",
"type": "Button",
"id": "9db494c5-bec3-4b09-bd5f-66ba07a3a729",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Tap,Hold",
"initialStateCheck": false
@ -81,7 +81,7 @@
"name": "Interactive",
"type": "Button",
"id": "9b32c7f1-0553-4735-b0f9-7726d59808ca",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Hold",
"initialStateCheck": false
@ -90,7 +90,7 @@
"name": "Melee",
"type": "Button",
"id": "0afd994b-f97e-4198-8700-5f570d3b7b56",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@ -99,7 +99,7 @@
"name": "Run",
"type": "Button",
"id": "95bba0fe-8e29-470a-a7ef-126b21ea4b5c",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,MultiTap",
"initialStateCheck": false
@ -108,7 +108,7 @@
"name": "Reload",
"type": "Button",
"id": "53f6beb0-aeec-43f0-aa36-f4d54f068d75",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@ -234,7 +234,7 @@
"name": "ToggleCamera",
"type": "Button",
"id": "a207eefa-8338-4ae0-95a1-8f650a3db235",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press",
"initialStateCheck": false
@ -272,6 +272,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "bcc014bd-8bca-4b71-96b9-62fa63db2d17",
"path": "<Pointer>/delta",
"interactions": "",
"processors": "",
"groups": "",
"action": "View",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "3204e30b-6062-47f0-9af6-e72db104c7ec",
@ -283,6 +294,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "88f9f36f-9b75-44a7-9176-b07a7daee4ab",
"path": "<XInputController>/buttonSouth",
"interactions": "",
"processors": "",
"groups": "",
"action": "Jump",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "c6f71c75-cb44-418b-9dc0-4202757d20ad",
@ -294,6 +316,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "2560f543-52ea-48c4-a2f6-df8b9deef518",
"path": "<XInputController>/buttonEast",
"interactions": "",
"processors": "",
"groups": "",
"action": "Crouch",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "ad8529d0-2894-4cb2-aea4-f07f96251032",
@ -316,6 +349,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "d158c65a-e04f-4a62-a576-d13796c3d903",
"path": "<Gamepad>/rightTrigger",
"interactions": "",
"processors": "",
"groups": "",
"action": "Fire",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "05431a8a-4464-49e0-8a15-eec2a4351985",
@ -327,6 +371,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "fce3a9d9-2f96-4a18-bc01-4f6a61f66e59",
"path": "<Gamepad>/leftTrigger",
"interactions": "",
"processors": "",
"groups": "",
"action": "Aim",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "9a305ecb-00ee-46fb-be5b-f58125ac34eb",
@ -338,6 +393,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "9a2d7939-d112-4796-a50f-7e723cb0ebb4",
"path": "<XInputController>/rightStickPress",
"interactions": "",
"processors": "",
"groups": "",
"action": "Melee",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "2D Vector",
"id": "f2793bde-975d-41cf-b721-4ea21c2cfe7d",
@ -415,6 +481,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "7475328b-a74c-4c33-90ad-0a59351a3595",
"path": "<XInputController>/leftStickPress",
"interactions": "",
"processors": "",
"groups": "",
"action": "Run",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "b8ca7195-54e8-472e-9849-378ea8b1b2d4",
@ -536,6 +613,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "fa054fa0-fc44-4096-a9df-ea3e6cae27f3",
"path": "<XInputController>/buttonWest",
"interactions": "",
"processors": "",
"groups": "",
"action": "Interactive",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "abfe62ef-1678-40c6-85e4-7644a3ffffc3",
@ -547,6 +635,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "ccb765ab-4913-467a-9b10-c66f7b1b836c",
"path": "<XInputController>/buttonWest",
"interactions": "Hold",
"processors": "",
"groups": "",
"action": "Reload",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "c354e7ce-0648-4b49-b042-1f7dfc7657d9",
@ -591,6 +690,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "438f83aa-f2a6-4d51-84a2-c6b334924fb0",
"path": "<XInputController>/select",
"interactions": "",
"processors": "",
"groups": "",
"action": "ToggleCamera",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "c7bf69ae-febf-4280-b153-d529229f82b5",
@ -686,7 +796,7 @@
"name": "Cancel",
"type": "Button",
"id": "60dfbf31-4ec8-4df1-be35-9f1eca07d7be",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Hold,Tap",
"initialStateCheck": false
@ -713,7 +823,7 @@
"name": "Inventory",
"type": "Button",
"id": "80289a66-dc26-4d9e-967d-a561bb8794e3",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Hold",
"initialStateCheck": false
@ -740,6 +850,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "16cf3841-290b-4e38-9b3f-71d4a5428c70",
"path": "<Gamepad>/leftStick",
"interactions": "",
"processors": "",
"groups": "",
"action": "Point",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "39028ab4-2f9f-43b2-99f6-6689cbc29e17",
@ -817,6 +938,50 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "c65bdae3-6e08-4931-b364-738eb5248f5c",
"path": "<Gamepad>/start",
"interactions": "",
"processors": "",
"groups": "",
"action": "Cancel",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "e610040c-2163-48b0-892a-d17c525b4763",
"path": "<VirtualMouse>/{Cancel}",
"interactions": "",
"processors": "",
"groups": "",
"action": "Cancel",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "835f92ac-0a3f-495c-a09b-694b13942d96",
"path": "<Touchscreen>/{Cancel}",
"interactions": "",
"processors": "",
"groups": "",
"action": "Cancel",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "a490bb73-90bd-4cd2-b702-cd89bc1987d4",
"path": "<Pointer>/{Cancel}",
"interactions": "",
"processors": "",
"groups": "",
"action": "Cancel",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "5f7a54af-6a1b-4e69-8a46-57a6772019cc",
@ -850,6 +1015,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "6f51a8c8-9f11-40d5-a0b9-3a78dde74a22",
"path": "<Gamepad>/dpad/up",
"interactions": "",
"processors": "",
"groups": "",
"action": "Inventory",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "WASD",
"id": "a40b1b7f-8cb3-4138-80a0-ec2771a2481a",
@ -981,7 +1157,7 @@
"name": "Vertical",
"type": "Button",
"id": "5703bb8a-1107-4f73-91fa-9bbc362d9528",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@ -1093,7 +1269,7 @@
"name": "AscendAndDescend",
"type": "Button",
"id": "a78acdb6-b9dd-4d90-a89e-58fb62647380",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@ -1309,7 +1485,7 @@
"name": "Vertical",
"type": "Button",
"id": "cc774816-362e-40d3-98c5-19411a7ae97a",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false

View File

@ -6,7 +6,6 @@ using System.IO;
using BITKit.IO;
using BITKit.UX;
using Cysharp.Threading.Tasks;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
@ -26,9 +25,11 @@ namespace BITKit
private static void Reload()
{
Stopwatch = new Stopwatch();
#if UNITY_EDITOR
#else
SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
#endif
}

View File

@ -0,0 +1,65 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit.Entities;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using UnityEngine;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace BITKit.IO
{
public class ScriptableEntitiesService : IDisposable
{
private readonly IEntitiesService _entitiesService;
public string Tags = "scriptable_object";
private readonly List<IEntity> _registeredEntities = new();
public ScriptableEntitiesService(IEntitiesService entitiesService)
{
_entitiesService = entitiesService;
}
public async UniTask InitializeAsync(ILogger logger = null)
{
logger?.LogInformation("正在查找所有ScriptableObject...");
var objs = await ModService.LoadAssets<ScriptableObject>(Tags);
logger?.LogInformation($"找到{objs.Count}个资源,正在加载中...");
for (var index = 0; index < objs.Count; index++)
{
var scriptableObject = objs[index];
var entity = new Entity();
var idComponent = new IdComponent();
entity.ServiceCollection.AddSingleton(idComponent);
var type = scriptableObject.GetType();
entity.ServiceCollection.AddSingleton(type, scriptableObject);
_entitiesService.Register(entity);
logger?.LogInformation($"已加载:{scriptableObject.name}:{type.Name},剩余:{index + 1}/{objs.Count}");
_registeredEntities.Add(entity);
}
logger?.LogInformation("加载完成");
}
public void Dispose()
{
foreach (var x in _registeredEntities)
{
_entitiesService.UnRegister(x);
}
_registeredEntities.Clear();
}
}
}

View File

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

View File

@ -1,30 +1,165 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.Logging;
using UnityEditor;
using UnityEngine;
using YooAsset;
using ILogger = Microsoft.Extensions.Logging.ILogger;
using Object = UnityEngine.Object;
namespace BITKit.IO
{
public class YooAssetModHelper : MonoBehaviour
public class YooAssetModHelper
{
private void OnEnable()
public static string Url = "http://server.bitfall.icu:21982/com.project.b/Mods";
public static readonly ConcurrentDictionary<string, HashSet<string>> PackagesManifestDictionary = new();
public static async UniTask LoadMods(ILogger logger=null)
{
ModService.LoadAssetAsyncFactory += LoadAsset;
try
{
var modPath = Path.Combine(Environment.CurrentDirectory, "Mods");
if (Application.platform is RuntimePlatform.Android)
{
modPath = Path.Combine($"/storage/emulated/0/{Application.identifier}/Mods/");
}
foreach (var directoryInfo in new DirectoryInfo(modPath).GetDirectories())
{
var packageName = directoryInfo.Name;
logger?.LogInformation($"开始加载:{packageName}");
var package = YooAssets.CreatePackage(packageName);
var initPars = new HostPlayModeParameters()
{
BuildinQueryServices = new GameQueryServices(),
RemoteServices = new RemoteServices($"{Url}/{directoryInfo.Name}",$"file://{directoryInfo.FullName}")
};
await package.InitializeAsync(initPars);
var update = package.UpdatePackageVersionAsync();
await update.ToUniTask();
var manifest = package.UpdatePackageManifestAsync(update.PackageVersion);
await manifest.ToUniTask();
var downloader = package.CreateResourceDownloader(10, 3);
if (logger is not null)
{
downloader.OnDownloadProgressCallback = (totalDownloadCount, currentDownloadCount,
totalDownloadBytes, currentDownloadBytes) =>
{
//下载进度
var progress = (float)currentDownloadBytes / totalDownloadBytes;
logger.LogInformation($"已下载{(int)(progress*100)}%,资源数量{currentDownloadBytes}/{totalDownloadBytes}");
};
downloader.OnDownloadErrorCallback = (fileName, error) =>
{
logger.LogError($"资源[{fileName}]下载错误::{error}");
};
}
downloader.BeginDownload();
await downloader.ToUniTask();
var mod = new MyMod()
{
FolderPath = directoryInfo.FullName,
PackageName = packageName,
Name = packageName
};
await ModService.Install(mod);
await ModService.Load(mod);
YooAssetUtils.RegisterResourcePackage(package);
logger?.LogInformation($"已加载:{packageName},路径:{directoryInfo.FullName}");
}
return;
}
catch (Exception e)
{
if (logger is not null)
{
logger.LogCritical(e.Message,e);
}
else
{
BIT4Log.LogException(e);
}
}
logger?.LogInformation($"未找到Mod");
}
private void OnDisable()
private static HashSet<string> BuildPackageCache(string obj)
{
ModService.LoadAssetAsyncFactory -= LoadAsset;
var package = YooAssets.GetPackage(obj);
var playMode = package.GetType().GetField("_playModeImpl", ReflectionHelper.Flags)!.GetValue(package);
var manifest = playMode.GetType().GetProperty("ActiveManifest", ReflectionHelper.Flags)!.GetValue(playMode);
var dictionary = manifest.GetType().GetField("AssetDic", ReflectionHelper.Flags)!.GetValue(manifest) as IDictionary;
var hashset = new HashSet<string>();
foreach (var key in dictionary.Keys)
{
hashset.Add(key?.ToString());
}
var assetPathMapping1=manifest.GetType().GetField("AssetPathMapping1", ReflectionHelper.Flags)!.GetValue(manifest) as IDictionary<string,string>;
foreach (var key in assetPathMapping1.Keys)
{
hashset.Add(key);
}
return hashset;
}
private static async UniTask<object> LoadAsset(string arg)
public static async UniTask<IReadOnlyList<object>> LoadAssets( string[] arg)
{
var handle = YooAssets.LoadAssetAsync(arg);
await handle;
return handle.AssetObject;
var list = new List<object>();
foreach (var resourcePackage in YooAssetUtils.RegisteredResourcePackages)
{
foreach (var assetInfo in resourcePackage.GetAssetInfos(arg))
{
var asyncHandle = resourcePackage.LoadAssetAsync(assetInfo);
await asyncHandle;
list.Add(asyncHandle.AssetObject);
}
}
return list;
}
public static async UniTask<object> LoadAsset(string arg)
{
foreach (var resourcePackage in YooAssetUtils.RegisteredResourcePackages.Reverse())
{
if(PackagesManifestDictionary.GetOrAdd(resourcePackage.PackageName,BuildPackageCache).Contains(arg) is false)continue;
var assetInfo = resourcePackage.GetAssetInfo(arg);
if(string.IsNullOrEmpty(assetInfo.Error) is false)continue;
var handle = resourcePackage.LoadAssetAsync(arg);
await handle;
return handle.AssetObject;
}
return null;
}
}
}

View File

@ -203,6 +203,14 @@ namespace BITKit
BITApp.Time.DeltaTime = Time.deltaTime;
BITApp.Time.TimeAsDouble = Time.timeAsDouble;
allowCursor = AllowCursor.Allow;
if (Touchscreen.current is not null && Touchscreen.current.IsPressed())
{
AllowCursor.AddElement(int.MaxValue);
}else if (Mouse.current is not null && Mouse.current.IsPressed())
{
AllowCursor.RemoveElement(int.MaxValue);
}
}
public string GetName()

View File

@ -36,6 +36,19 @@ namespace BITKit
action.RegisterCallback(callback);
return this;
}
public InputActionGroup RegisterCallback(InputAction inputAction,Action<InputAction.CallbackContext> callback)
{
EnsureConfiguration();
var action = actions.GetOrAdd(inputAction.name, _=>inputAction.Clone());
allowInput.Invoke();
action.RegisterCallback(callback);
return this;
}
public InputAction EnsureCreated(InputActionReference reference)
{
@ -57,6 +70,20 @@ namespace BITKit
return action;
}
public InputAction EnsureCreated(InputAction inputAction)
{
EnsureConfiguration();
var action = actions.GetOrAdd(inputAction.name, _ =>
{
var newAction = inputAction.Clone();
newAction.Rename(inputAction.name);
return newAction;
});
allowInput.Invoke();
return action;
}
public void Inherit(InputActionGroup other)
{
@ -80,6 +107,12 @@ namespace BITKit
action.UnRegisterCallback(callback);
}
public void UnRegisterCallback(InputAction inputAction, Action<InputAction.CallbackContext> callback)
{
if(actions.TryGetValue(inputAction.name,out var action))
action.UnRegisterCallback(callback);
}
private void EnsureConfiguration()
{
if (state is not InitializationState.Initialized)

View File

@ -0,0 +1,50 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit;
using UnityEngine;
namespace UnityEngine.InputSystem
{
public class UnityPlayerInput : MonoBehaviour
{
private readonly InputActionGroup _inputActionGroup=new();
[SerializeField] private InputActionAsset inputActionAsset;
private bool _isInitialized;
private void Start()
{
foreach (var inputActionMap in inputActionAsset.actionMaps)
{
foreach (var inputAction in inputActionMap.actions)
{
_inputActionGroup.RegisterCallback(inputAction, x =>
{
SendMessage($"On{inputAction.name}",x,SendMessageOptions.DontRequireReceiver);
});
}
}
if (_isInitialized is false)
{
_inputActionGroup.allowInput.AddElement(this);
}
_isInitialized = true;
}
private void OnEnable()
{
if (_isInitialized)
_inputActionGroup.allowInput.AddElement(this);
}
private void OnDisable()
{
_inputActionGroup.allowInput.RemoveElement(this);
}
}
}

View File

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

View File

@ -1,121 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BITKit.UX;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace BITKit.Mod
{
public class UnityModService : MonoBehaviour
{
[SerializeReference,SubclassSelector] private IReference[] referencedAssemblies;
private async void Start()
{
//DI.TryGet<IUXWaiting>(out var waiting);
IUXWaiting waiting = null;
var handle = waiting?.Get();
handle?.SetMessage("正在初始化Mod服务");
if (Application.isEditor is false)
{
BIT4Log.Log<UnityModService>($"UnityPlayer所在位置:{Application.dataPath}");
BIT4Log.Log<UnityModService>($"{nameof(System.Linq)}位于{typeof(Enumerable).Assembly.Location}");
}
foreach (var x in referencedAssemblies)
{
var dllName = x.Value.Contains(".dll") ? x.Value : $"{x.Value}.dll";
if (SearchDll(dllName,out var dll) is false)
{
BIT4Log.Warning<UnityModService>($"未找到:{dll}");
continue;
}
BITSharp.ReferencedAssemblies.Add(@$"""{dll}""");
}
try
{
ModService.Initialize();
}
catch (Exception e)
{
BIT4Log.Warning<UnityModService>("初始化失败");
BIT4Log.LogException(e);
return;
}
destroyCancellationToken.Register(ModService.Dispose);
ModService.OnPackageLoad+=OnPackageLoad;
var packages = await ModService.SearchPackages();
if (destroyCancellationToken.IsCancellationRequested) return;
foreach (var package in packages)
{
await ModService.LoadFromPackage(package.PackagePath);
if (destroyCancellationToken.IsCancellationRequested) return;
}
destroyCancellationToken.Register(() =>
{
ModService.OnPackageLoad-=OnPackageLoad;
});
waiting?.Release(handle);
}
private void OnPackageLoad(ModPackage obj)
{
var loadedDlls = referencedAssemblies.Cast();
var reportBuilder = new System.Text.StringBuilder();
//对比已加载的dll和当前引用的dll
foreach (var x in obj.Dlls.Except(loadedDlls))
{
if (SearchDll(x, out var dll) is false)
{
BIT4Log.Warning<UnityModService>($"未找到:{dll}");
continue;
}
BITSharp.ReferencedAssemblies.Add(@$"""{dll}""");
reportBuilder.AppendLine($"加载:{dll}");
}
BIT4Log.Log<UnityModService>(reportBuilder.ToString());
}
private bool SearchDll(string dllName,out string dll,params string[] moreFolder)
{
#if UNITY_EDITOR
dll = System.IO.Path.Combine(Environment.CurrentDirectory, "Library", "ScriptAssemblies", dllName);
var folder = EditorApplication.applicationPath;
folder = Path.GetDirectoryName(folder);
if(File.Exists(dll) is false)
{
dll = Path.Combine(folder,"Data", "MonoBleedingEdge", "lib","mono","unityjit-win32",dllName);
}
if (File.Exists(dll) is false)
{
dll = Path.Combine(folder,"Data", "MonoBleedingEdge", "lib","mono","unityjit-win32","Facades",dllName);
}
#else
dll = System.IO.Path.Combine(Environment.CurrentDirectory,$"{Application.productName}_Data", "Managed", dllName);
#endif
return File.Exists(dll);
}
}
}

View File

@ -1,48 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace BITKit.Mod
{
public class UnityModServiceTester : MonoBehaviour
{
[Serializable]
public class TestLogGameTickMod:MyMod
{
public override void OnInitialized()
{
base.OnInitialized();
GameTickService.Add(OnTick);
}
private static void OnTick(float obj)
{
BIT4Log.Log<TestLogGameTickMod>($"On Test Mod Tick,delta:{obj}");
}
public override void OnDispose()
{
GameTickService.Remove(OnTick);
}
}
[SerializeReference, SubclassSelector] private IMod[] initialMods;
private void OnEnable()
{
foreach (var testMod in initialMods)
{
ModService.Install(testMod);
ModService.Load(testMod);
}
}
private void OnDisable()
{
foreach (var testMod in initialMods)
{
ModService.UnLoad(testMod);
ModService.UnInstall(testMod);
}
}
}
}

View File

@ -2,7 +2,8 @@
"name": "BITKit.Physics",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4"
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:6babdba9f8b742f40904649736008000"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -0,0 +1,99 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.Physics
{
public static class GeometryUtils
{
/// <summary>
/// 获取一个点到一条线段的最近点
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static Vector3 PointToLineSegmentDistance(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
{
Vector3 lineDirection = lineEnd - lineStart;
Vector3 pointDirection = point - lineStart;
float lineLength = lineDirection.magnitude;
lineDirection.Normalize();
float dotProduct = Vector3.Dot(pointDirection, lineDirection);
dotProduct = Mathf.Clamp(dotProduct, 0f, lineLength);
Vector3 closestPoint = lineStart + lineDirection * dotProduct;
return closestPoint;
}
/// <summary>
/// 获取一个点到一个三角形内最短距离的点
/// </summary>
/// <param name="a">三角形顶点a</param>
/// <param name="b">三角形顶点b</param>
/// <param name="c">三角形顶点c</param>
/// <param name="pos"></param>
/// <returns></returns>
public static Vector3 GetPosInTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 pos)
{
Vector3 normal = Vector3.Cross(b - a, c - a).normalized;
Vector3 toPoint = pos - a;
float distance = Vector3.Dot(toPoint, normal);
Vector3 targetPos = pos - distance * normal;
if(PointInTriangle(targetPos, a, b, c))
return targetPos;
else
{
Vector3 p1 = PointToLineSegmentDistance(pos, a, b);
Vector3 p2 = PointToLineSegmentDistance(pos, a, c);
Vector3 p3 = PointToLineSegmentDistance(pos, b, c);
float d1 = Vector3.Distance(p1, pos);
float d2 = Vector3.Distance(p2, pos);
float d3 = Vector3.Distance(p3, pos);
if (d1 <= d2 && d1 <= d3)
return p1;
else if (d2 <= d3 && d2 <= d1)
return p2;
else /*if(d3 <= d1 && d3 <= d2)*/
return p3;
//return default;
}
}
/// <summary>
/// 判断一个点是否在三角形内
/// </summary>
/// <param name="pos"></param>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <returns></returns>
public static bool PointInTriangle(Vector3 pos, Vector3 a, Vector3 b, Vector3 c)
{
var v0 = c - a;
var v1 = b - a;
var v2 = pos - a;
var dot00 = Vector3.Dot(v0, v0);
var dot01 = Vector3.Dot(v0, v1);
var dot02 = Vector3.Dot(v0, v2);
var dot11 = Vector3.Dot(v1, v1);
var dot12 = Vector3.Dot(v1, v2);
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// 如果u和v都在[0,1]的范围内那么点P在三角形ABC内
return (u >= 0) && (v >= 0) && (u + v < 1);
}
}
}

View File

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

View File

@ -1,7 +1,9 @@
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using DrawXXL;
using UnityEngine;
namespace BITKit.Physics
@ -9,7 +11,7 @@ namespace BITKit.Physics
public class GetClosePointFromCollider : IClosePointProvider
{
public string Name="Default";
public Transform Transform;
public readonly Transform Transform;
public Vector3 Offset = default;
public LayerMask LayerMask=LayerMask.NameToLayer("Default");
public float Distance=1.6f;
@ -26,68 +28,97 @@ namespace BITKit.Physics
public bool TryGetValue(out Vector3 position, out Collider collider)
{
Vector3 vector3 = default;
StringBuilder reportBuilder = new();
reportBuilder.AppendLine($"检测任务:{Name}");
position = Transform.position + Transform.rotation * Offset;
var detectedLength = UnityEngine.Physics.OverlapSphereNonAlloc(position, Distance, _mainCollider, LayerMask);
reportBuilder.AppendLine($"检测到了{detectedLength}个碰撞体");
var validMeshColliders = new Queue<(Collider collider,Vector3 targetPosition)>();
var samplePoint = position;
samplePoint.y += Distance / 2;
foreach (var collider1 in _mainCollider.Take(detectedLength).OrderBy(ByTop).Reverse())
//for (var i = 0; i <detectedLength ; i++)
{
//reportBuilder.AppendLine($"----------------------------检测到了碰撞体{_mainCollider[i].name}");
//var collider = _mainCollider[i];
if (collider1.isTrigger)
{
reportBuilder?.AppendLine("碰撞体是触发器");
continue;
}
Vector3 closePoint;
switch (collider1)
{
case MeshCollider meshCollider:
if (meshCollider.convex is false)
{
reportBuilder?.AppendLine("MeshCollider未勾选Convex");
continue;
}
case TerrainCollider terrainCollider:
{
closePoint = terrainCollider.ClosestPointOnBounds(samplePoint);
}
break;
case MeshCollider { convex: false } meshCollider:
{
var getClosestPointFromMesh =
new GetClosestPointFromMesh(meshCollider.sharedMesh,meshCollider.transform.InverseTransformPoint(samplePoint));
break;
if (getClosestPointFromMesh.TryGetValue(out var localPosition, out collider))
{
localPosition = meshCollider.transform.TransformPoint(localPosition);
Debug.DrawLine(Transform.position,localPosition,Color.magenta);
if (Vector3.Distance(localPosition, position) < Distance)
{
closePoint = localPosition;
}
else
{
continue;
}
}
else
{
continue;
}
}
break;
default:
closePoint = collider1.ClosestPoint(samplePoint);
break;
}
if(collider1.Raycast(new Ray(Transform.position,Transform.forward),out var raycastHit,64) is false)
{
continue;
}
if (MathV.IsForward(position, Transform.forward, closePoint) is false)
{
//DrawBasics.PointTag(closePoint,"not forward");
continue;
}
var bounds = collider1.bounds;
vector3 = collider1.ClosestPoint(Transform.position + Vector3.up * 64);
var top = bounds.center.y + bounds.extents.y;
Debug.DrawLine(Transform.position, Transform.position + Vector3.up * top, Color.blue, 8f);
if (Transform.position.y + MinHeight > top)
if (Transform.position.y + MinHeight > closePoint.y)
{
reportBuilder?.AppendLine("高度不足");
DrawBasics.PointTag(closePoint,"not enough height");
continue;
}
var nextPos = position;
nextPos.y = collider1.bounds.center.y;
{
var ray = new Ray(nextPos, Transform.forward);
if (collider1.Raycast(new Ray(nextPos, Transform.forward), out _, Distance) is false)
{
reportBuilder?.AppendLine("未检测到前方");
Debug.DrawRay(ray.origin,ray.direction,Color.red,Distance);
continue;
}
}
var height = Mathf.Abs(top - Transform.position.y);
var height = Mathf.Abs(closePoint.y - Transform.position.y);
if (height > MaxHeight)
{
reportBuilder?.AppendLine($"高度差距过大:{height}");
continue;
}
if (UnityEngine.Physics.Linecast(Transform.position, vector3, out var raycastHit2, LayerMask))
if (UnityEngine.Physics.Linecast(Transform.position, closePoint, out var raycastHit2, LayerMask))
{
if (raycastHit2.collider != collider1)
{
@ -95,40 +126,61 @@ namespace BITKit.Physics
continue;
}
}
var length = UnityEngine.Physics.OverlapSphereNonAlloc(vector3, 0.01f, _colliders, LayerMask);
var length = UnityEngine.Physics.OverlapSphereNonAlloc(closePoint+Vector3.up*0.2f, 0.1f, _colliders, LayerMask);
switch (length)
{
case 1:
if (_colliders[0] != collider1)
{
reportBuilder.AppendLine($"检测到了其他碰撞体{_colliders[0].name}");
continue;
}
break;
case > 1:
case > 0:
reportBuilder.AppendLine("检测到了更多碰撞体");
for (var ii = 0; ii < length; ii++)
{
//Debug.DrawLine(vector3, _colliders[ii].ClosestPoint(vector3), Color.red, 8);
reportBuilder.AppendLine($"\t{_colliders[ii].name}");
}
continue;
}
vector3.y = top;
position = vector3;
collider = collider1;
reportBuilder.AppendLine("<color=green>成功</color>");
//BIT4Log.Log<GetClosePointFromCollider>(reportBuilder.ToString());
return true;
Debug.DrawLine(Transform.position,closePoint,Color.green);
validMeshColliders.Enqueue(new(collider1,closePoint));
}
var minDot = 64f;
Collider resultCollider = default;
Vector3 resultPosition=default;
while (validMeshColliders.TryDequeue(out var result))
{
var dot =Mathf.Abs(Vector3.Cross(Transform.forward, result.targetPosition-Transform.position).y);
DrawBasics.LineFrom(Transform.position,result.targetPosition-Transform.position,Color.red,text:dot.ToString(CultureInfo.InvariantCulture));
if(dot>minDot)continue;
resultCollider = result.collider;
resultPosition = result.targetPosition;
minDot = dot;
}
if (minDot < 64)
{
collider = resultCollider;
position = resultPosition;
return true;
}
collider = null;
//BIT4Log.Log<GetClosePointFromCollider>(reportBuilder.ToString());
return false;
}
private float ByTop(Collider arg)

View File

@ -0,0 +1,34 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.Physics
{
[ExecuteAlways]
public class GetClosePointFromColliderDebugger : MonoBehaviour
{
private GetClosePointFromCollider _getClosePointFromCollider;
[SerializeField] private LayerMask layerMask;
private void OnEnable()
{
_getClosePointFromCollider = new GetClosePointFromCollider(transform);
}
private void Update()
{
_getClosePointFromCollider.LayerMask = layerMask;
if (_getClosePointFromCollider.TryGetValue(out Vector3 vector3, out var collider1))
{
var offset = Vector3.up * -0.01f;
Debug.DrawLine(transform.position+offset,vector3+offset,Color.cyan);
}
}
private void OnDrawGizmosSelected()
{
Gizmos.DrawWireSphere(transform.position,_getClosePointFromCollider.Distance);
}
}
}

View File

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

View File

@ -0,0 +1,53 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace BITKit.Physics
{
public readonly struct GetClosestPointFromMesh:IClosePointProvider
{
private readonly Vector3 _position;
private readonly Mesh _mesh;
public GetClosestPointFromMesh(Mesh mesh, Vector3 position)
{
_mesh = mesh;
_position = position;
}
public bool TryGetValue(out Vector3 position, out Collider collider)
{
position = default;
collider = default;
if (_mesh.isReadable is false) return false;
var vertices = _mesh.vertices;
if (vertices.Length > 2048) return false;
var minPos = new Vector3(64, 64, 64);
for (var index = 0; index < _mesh.triangles.Length; index+=3)
{
var x = vertices[_mesh.triangles[index]];
var y = vertices[_mesh.triangles[index + 1]];
var z = vertices[_mesh.triangles[index + 2]];
var pos = GeometryUtils.GetPosInTriangle(x, y, z, _position);
if (Vector3.Distance(pos, _position) < Vector3.Distance(minPos, _position))
{
minPos = pos;
}
}
position = minPos;
return true;
}
}
}

View File

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

View File

@ -27,7 +27,7 @@ namespace BITKit
AssetDatabase.LoadAssetAtPath<DictionaryReferenceScriptableObject>(
"Assets/Artists/Configs/reference_dictionary.asset");
#else
var task = YooAssets.LoadAssetAsync("reference_directory");
var task = YooAssets.LoadAssetAsync("reference_dictionary");
task.WaitForAsyncComplete();
_singleton=task.AssetObject as DictionaryReferenceScriptableObject;
#endif

View File

@ -11,7 +11,7 @@ using UnityEngine;
using UnityEngine.Jobs;
using UnityEngine.Pool;
namespace BITFALL.Rig{
namespace BITKit.UX.Rig{
public class TickOverrideTranformService : MonoBehaviour
{
//[BurstCompile]

View File

@ -3,7 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITFALL.Rig
namespace BITKit.UX.Rig
{
public class TickOverrideTransform : MonoBehaviour
{

View File

@ -14,7 +14,7 @@ namespace BITKit
[Serializable]
public class IntervalTick:ITicker
{
[SerializeField] private float interval;
private float interval;
public ulong TickCount=>IntervalTickService.GetTickCount(interval);
public void Add(Action action)

View File

@ -18,7 +18,9 @@
"GUID:517785bb4600a5140b47eac5fa49b8fc",
"GUID:838d3286f0973344ab6e99d3951012f7",
"GUID:a11ff146d38b27a44af87b4b4d9c4ecb",
"GUID:e4d11af1289097a4d9d8987f332a2ae8"
"GUID:e4d11af1289097a4d9d8987f332a2ae8",
"GUID:3abaaefa7af558d44ba20cea1c43d602",
"GUID:d8b63aba1907145bea998dd612889d6b"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9d540311d69835747a74085c9e0aba6c
guid: ea593ccd503f23741b162c0f5a00fb52
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -59,9 +59,10 @@ namespace BITKit.UX
GUILayout.BeginVertical();
//颜色更改为黑色
GUI.color = textColor;
GUILayout.Label(_logBuilder.ToString(),style);
GUILayout.Label(string.Join("\n",_logBuilder.ToString().Split("\n").Reverse()),style);
GUILayout.EndVertical();
GUILayout.EndArea();
}
}
}

View File

@ -8,7 +8,8 @@
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:517785bb4600a5140b47eac5fa49b8fc"
"GUID:517785bb4600a5140b47eac5fa49b8fc",
"GUID:d8b63aba1907145bea998dd612889d6b"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -0,0 +1,115 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class OnScreenButton : OnScreenControl
{
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlFloatAttributeDescription m_PressedValueAttribute = new ()
{
name = "PressedValue",
defaultValue = 1f,
};
private readonly UxmlBoolAttributeDescription m_ReleasePressAttribute = new()
{
name = "ReleasePress"
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var table = (OnScreenButton)ve;
table.PressedValue = m_PressedValueAttribute.GetValueFromBag(bag, cc);
table.ReleasePress = m_ReleasePressAttribute.GetValueFromBag(bag, cc);
}
}
public new class UxmlFactory : UxmlFactory<OnScreenButton, UxmlTraits> { }
public float PressedValue { get; set; }= 1f;
public bool ReleasePress { get; set; }
private bool _isPressed;
private int _pointerId=-1;
private Label _label;
private readonly ValidHandle _isTriggered = new();
public OnScreenButton()
{
RegisterCallback<PointerUpEvent>(OnPointerUp);
RegisterCallback<PointerDownEvent>(OnPointerDown);
RegisterCallback<PointerMoveEvent>(OnPointerMove);
this.AddManipulator(new Clickable(OnClick));
_label = this.Create<Label>();
_isTriggered.AddListener(x =>
{
if (x)
{
AddToClassList("selected");
}
else
{
RemoveFromClassList("selected");
}
var value = x?PressedValue : 0f;
SendValueToControl( value);
});
}
private void OnClick()
{
}
private void OnPointerMove(PointerMoveEvent evt)
{
if(_pointerId!=evt.pointerId)return;
if (ReleasePress)
{
_isPressed = true;
return;
}
if (_isPressed is false)
{
_isTriggered.AddElement(0);
_isPressed = true;
;
}
}
private void OnPointerDown(PointerDownEvent evt)
{
if(_pointerId is not -1)return;
_pointerId = evt.pointerId;
_isPressed = true;
_isTriggered.AddElement(0);
}
private void OnPointerUp(PointerUpEvent evt)
{
if(_pointerId!=evt.pointerId)return;
if (ReleasePress && _isPressed)
{
_isTriggered.AddElement(0);
_pointerId = -1;
return;
}
_pointerId = -1;
_isTriggered.RemoveElement(0);
}
protected override string ControlPathInternal { get; set; }
}
}

View File

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

View File

@ -0,0 +1,330 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public abstract class OnScreenControl:VisualElement
{
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlStringAttributeDescription m_ControlPathAttribute = new ()
{
name = "ControlPath"
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var table = (OnScreenControl)ve;
table.ControlPath = m_ControlPathAttribute.GetValueFromBag(bag, cc);
}
}
protected OnScreenControl()
{
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanel);
IsEnabled.AddDisableElements(this);
IsEnabled.AddListener(x =>
{
if (x)
OnEnable();
else OnDisable();
});
}
private void OnDetachFromPanel(DetachFromPanelEvent evt)
{
IsEnabled.AddDisableElements(32);
}
private void OnAttachToPanel(AttachToPanelEvent evt)
{
IsEnabled.RemoveDisableElements(32);
}
private void OnGeometryChanged(GeometryChangedEvent evt)
{
IsEnabled.SetDisableElements(this, !visible);
}
protected readonly ValidHandle IsEnabled = new();
/// <summary>
/// The control path (see <see cref="InputControlPath"/>) for the control that the on-screen
/// control will feed input into.
/// </summary>
/// <remarks>
/// A device will be created from the device layout referenced by the control path (see
/// <see cref="InputControlPath.TryGetDeviceLayout"/>). The path is then used to look up
/// <see cref="Control"/> on the device. The resulting control will be fed values from
/// the on-screen control.
///
/// Multiple on-screen controls sharing the same device layout will together create a single
/// virtual device. If, for example, one component uses <c>"&lt;Gamepad&gt;/buttonSouth"</c>
/// and another uses <c>"&lt;Gamepad&gt;/leftStick"</c> as the control path, a single
/// <see cref="Gamepad"/> will be created and the first component will feed data to
/// <see cref="Gamepad.buttonSouth"/> and the second component will feed data to
/// <see cref="Gamepad.leftStick"/>.
/// </remarks>
/// <seealso cref="InputControlPath"/>
public string ControlPath
{
get => ControlPathInternal;
set
{
IsEnabled.SetElements(1, string.IsNullOrEmpty(value) is false);
ControlPathInternal = value;
IsEnabled.AddDisableElements("Force");
IsEnabled.RemoveDisableElements("Force");
}
}
/// <summary>
/// The actual control that is fed input from the on-screen control.
/// </summary>
/// <remarks>
/// This is only valid while the on-screen control is enabled. Otherwise, it is <c>null</c>. Also,
/// if no <see cref="ControlPath"/> has been set, this will remain <c>null</c> even if the component is enabled.
/// </remarks>
public InputControl Control => _mControl;
private InputControl _mControl;
private OnScreenControl _mNextControlOnDevice;
private InputEventPtr _mInputEventPtr;
/// <summary>
/// Accessor for the <see cref="ControlPath"/> of the component. Must be implemented by subclasses.
/// </summary>
/// <remarks>
/// Moving the definition of how the control path is stored into subclasses allows them to
/// apply their own <see cref="InputControlAttribute"/> attributes to them and thus set their
/// own layout filters.
/// </remarks>
protected abstract string ControlPathInternal { get; set; }
private void SetupInputControl()
{
Debug.Assert(_mControl == null, "InputControl already initialized");
Debug.Assert(_mNextControlOnDevice == null, "Previous InputControl has not been properly uninitialized (m_NextControlOnDevice still set)");
Debug.Assert(!_mInputEventPtr.valid, "Previous InputControl has not been properly uninitialized (m_InputEventPtr still set)");
// Nothing to do if we don't have a control path.
var path = ControlPathInternal;
if (string.IsNullOrEmpty(path))
return;
// Determine what type of device to work with.
var layoutName = InputControlPath.TryGetDeviceLayout(path);
if (layoutName == null)
{
Debug.LogError(
$"Cannot determine device layout to use based on control path '{path}' used in {GetType().Name} component with {name}");
return;
}
// Try to find existing on-screen device that matches.
var internedLayoutName = new InternedString(layoutName);
var deviceInfoIndex = -1;
for (var i = 0; i < OnScreenDevices.Count; ++i)
{
////FIXME: this does not take things such as different device usages into account
if (OnScreenDevices[i].Device.layout != internedLayoutName) continue;
deviceInfoIndex = i;
break;
}
// If we don't have a matching one, create a new one.
InputDevice device;
if (deviceInfoIndex == -1)
{
// Try to create device.
try
{
device = InputSystem.AddDevice(layoutName);
}
catch (Exception exception)
{
Debug.LogError(
$"Could not create device with layout '{layoutName}' used in '{GetType().Name}' component");
Debug.LogException(exception);
return;
}
InputSystem.AddDeviceUsage(device, "OnScreen");
// Create event buffer.
var buffer = StateEvent.From(device, out var eventPtr, Allocator.Persistent);
// Add to list.
deviceInfoIndex = OnScreenDevices.Count;
OnScreenDevices.Add(new OnScreenDeviceInfo
{
EventPtr = eventPtr,
Buffer = buffer,
Device = device,
});
}
else
{
device = OnScreenDevices[deviceInfoIndex].Device;
}
// Try to find control on device.
_mControl = InputControlPath.TryFindControl(device, path);
if (_mControl == null)
{
Debug.LogError(
$"Cannot find control with path '{path}' on device of type '{layoutName}' referenced by component '{GetType().Name}' with {name}");
// Remove the device, if we just created one.
if (OnScreenDevices[deviceInfoIndex].FirstControl == null)
{
OnScreenDevices[deviceInfoIndex].Destroy();
OnScreenDevices.RemoveAt(deviceInfoIndex);
}
return;
}
_mInputEventPtr = OnScreenDevices[deviceInfoIndex].EventPtr;
// We have all we need. Permanently add us.
OnScreenDevices[deviceInfoIndex] =
OnScreenDevices[deviceInfoIndex].AddControl(this);
}
protected void SendValueToControl<TValue>(TValue value)
where TValue : struct
{
if (_mControl == null)
return;
if (!(_mControl is InputControl<TValue> control))
throw new ArgumentException(
$"The control path {ControlPath} yields a control of type {_mControl.GetType().Name} which is not an InputControl with value type {typeof(TValue).Name}", nameof(value));
////FIXME: this gives us a one-frame lag (use InputState.Change instead?)
_mInputEventPtr.time = InputState.currentTime;
control.WriteValueIntoEvent(value, _mInputEventPtr);
InputSystem.QueueEvent(_mInputEventPtr);
}
protected void SentDefaultValueToControl()
{
if (_mControl == null)
return;
////FIXME: this gives us a one-frame lag (use InputState.Change instead?)
_mInputEventPtr.time = InputState.currentTime;
_mControl.ResetToDefaultStateInEvent(_mInputEventPtr);
InputSystem.QueueEvent(_mInputEventPtr);
}
protected virtual void OnEnable()
{
SetupInputControl();
}
protected virtual void OnDisable()
{
if (_mControl == null)
return;
var device = _mControl.device;
for (var i = 0; i < OnScreenDevices.Count; ++i)
{
if (OnScreenDevices[i].Device != device)
continue;
var deviceInfo = OnScreenDevices[i].RemoveControl(this);
if (deviceInfo.FirstControl == null)
{
// We're the last on-screen control on this device. Remove the device.
OnScreenDevices[i].Destroy();
OnScreenDevices.RemoveAt(i);
}
else
{
OnScreenDevices[i] = deviceInfo;
// We're keeping the device , but we're disabling the on-screen representation
// for one of its controls. If the control isn't in default state, reset it
// to that now. This is what ensures that if, for example, OnScreenButton is
// disabled after OnPointerDown, we reset its button control to zero even
// though we will not see an OnPointerUp.
if (!_mControl.CheckStateIsAtDefault())
SentDefaultValueToControl();
}
_mControl = null;
_mInputEventPtr = new InputEventPtr();
Debug.Assert(_mNextControlOnDevice == null);
break;
}
}
private struct OnScreenDeviceInfo
{
public InputEventPtr EventPtr;
public NativeArray<byte> Buffer;
public InputDevice Device;
public OnScreenControl FirstControl;
public OnScreenDeviceInfo AddControl(OnScreenControl control)
{
control._mNextControlOnDevice = FirstControl;
FirstControl = control;
return this;
}
public OnScreenDeviceInfo RemoveControl(OnScreenControl control)
{
if (FirstControl == control)
FirstControl = control._mNextControlOnDevice;
else
{
for (OnScreenControl current = FirstControl._mNextControlOnDevice, previous = FirstControl;
current != null; previous = current, current = current._mNextControlOnDevice)
{
if (current != control)
continue;
previous._mNextControlOnDevice = current._mNextControlOnDevice;
break;
}
}
control._mNextControlOnDevice = null;
return this;
}
public void Destroy()
{
if (Buffer.IsCreated)
Buffer.Dispose();
if (Device != null)
InputSystem.RemoveDevice(Device);
Device = null;
Buffer = new NativeArray<byte>();
}
}
private static readonly List<OnScreenDeviceInfo> OnScreenDevices=new ();
internal string GetWarningMessage()
{
return $"{GetType()} needs to be attached as a child to a UI Canvas and have a RectTransform component to function properly.";
}
}
}

View File

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

View File

@ -0,0 +1,92 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class OnScreenGamepad : OnScreenControl
{
public new class UxmlFactory : UxmlFactory<OnScreenGamepad, UxmlTraits> { }
protected override string ControlPathInternal { get; set; }
private readonly Dictionary<Vector2Int, VisualElement> _buttonMap = new();
public OnScreenGamepad()
{
IsEnabled.AddListener(OnActive);
for (var y = 1; y >=-1; y--)
{
var row = this.Create<VisualElement>();
row.style.flexGrow = 1;
row.style.flexDirection = FlexDirection.Row;
for (var x = -1; x <= 1; x++)
{
var dir = (x, y) switch
{
(-1, 1) => "NW", // 西北
(0, 1) => "N", // 北
(1, 1) => "NE", // 东北
(-1, 0) => "W", // 西
(0, 0) => "C", // 中心
(1, 0) => "E", // 东
(-1, -1) => "SW", // 西南
(0, -1) => "S", // 南
(1, -1) => "SE", // 东南
_ => "NULL" // 其他无效位置
};
var value = (x, y) switch
{
(-1, -1) => new float2(-0.707f, -0.707f), // NW 西北
(0, -1) =>new float2 (0f, -1f), // N 北
(1, -1) =>new float2 (0.707f, -0.707f), // NE 东北
(-1, 0) => new float2(-1f, 0f), // W 西
(0, 0) =>new float2 (0f, 0f), // C 中心
(1, 0) =>new float2 (1f, 0f), // E 东
(-1, 1) => new float2(-0.707f, 0.707f), // SW 西南
(0, 1) =>new float2 (0f, 1f), // S 南
(1, 1) => new float2(0.707f, 0.707f), // SE 东南
_ => new float2(0f, 0f) // 默认返回 (0, 0) 无效位置
};
var button = row.Create<VisualElement>();
_buttonMap.TryAdd(new Vector2Int(x, y), button);
button.style.flexGrow = 1;
button.AddToClassList("gamepad-button");
button.AddToClassList($"gamepad-button--{dir.ToLower()}");
button.AddManipulator(new Clickable(()=>{}));
button.RegisterCallback<PointerOverEvent>(_ =>
{
SendValueToControl((Vector2)value);
});
button.RegisterCallback<PointerUpEvent>(_ =>
{
SendValueToControl((Vector2)default);
});
// var label = button.Create<Label>();
// label.style.flexGrow = 1;
//
// label.text = $"{value.x},{value.y}";
}
}
}
private void OnActive(bool obj)
{
if (obj is false)
{
SendValueToControl((Vector2)default);
}
}
}
}

View File

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

View File

@ -0,0 +1,80 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class OnScreenStick:OnScreenControl
{
public new class UxmlTraits : OnScreenControl.UxmlTraits
{
private readonly UxmlBoolAttributeDescription m_IsDelteaAttribute = new ()
{
name = "IsDelta"
};
private readonly UxmlFloatAttributeDescription m_MoveRangeAttribute = new()
{
name = "MoveRange",defaultValue = 32,
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var table = (OnScreenStick)ve;
table.IsDelta = m_IsDelteaAttribute.GetValueFromBag(bag, cc);
table.MoveRange=m_MoveRangeAttribute.GetValueFromBag(bag, cc);
}
}
public new class UxmlFactory : UxmlFactory<OnScreenStick, UxmlTraits> { }
public bool IsDelta { get; set; }
public float MoveRange { get; set; } = 32;
protected override string ControlPathInternal { get; set; }
private int _ignoreFrame=1;
private Vector2 _startPosition;
public OnScreenStick()
{
RegisterCallback<PointerDownEvent>(OnPointerDown);
RegisterCallback<PointerMoveEvent>(OnPointerMove);
RegisterCallback<PointerUpEvent>(OnPointerUp);
}
private void OnPointerUp(PointerUpEvent evt)
{
SendValueToControl(Vector2.zero);
_startPosition = evt.position;
_ignoreFrame = 1;
}
private void OnPointerDown(PointerDownEvent evt)
{
_ignoreFrame = 1;
_startPosition = evt.position;
}
private void OnPointerMove(PointerMoveEvent evt)
{
if (_ignoreFrame-- > 0)
{
return;
}
if (Vector2.Distance(evt.deltaPosition, default) > Vector2.Distance(_startPosition, evt.position))
{
return;
}
var pos = evt.deltaPosition;
if (IsDelta)
{
var newPos = evt.position;
pos = new Vector2(newPos.x, newPos.y) - _startPosition;
pos /= MoveRange;
}
SendValueToControl(new Vector2(pos.x, -pos.y));
}
}
}

View File

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

View File

@ -11,9 +11,9 @@ namespace BITKit.UX
{
public UXInputAction(VisualElement visualElement,string controlPathInternal)
{
this.controlPathInternal = controlPathInternal;
this.ControlPathInternal = controlPathInternal;
}
protected sealed override string controlPathInternal { get; set; }
protected sealed override string ControlPathInternal { get; set; }
}
}

View File

@ -0,0 +1,52 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class CellContainer : VisualElement
{
public new class UxmlFactory : UxmlFactory<CellContainer, UxmlTraits> { }
public int CellSize { get; set; } = 64;
public CellContainer()
{
RegisterCallback<CustomStyleResolvedEvent>(x =>
{
MarkDirtyRepaint();
});
generateVisualContent += GenerateVisualContent;
}
private void GenerateVisualContent(MeshGenerationContext obj)
{
if(contentRect.height<=0 || contentRect.width<=0)return;
var painter = obj.painter2D;
painter.lineWidth = resolvedStyle.borderTopWidth;
painter.strokeColor = resolvedStyle.borderTopColor;
var yCount = contentRect.height / CellSize;
var xCount = contentRect.width / CellSize;
for (var x = 1; x < xCount; x++)
{
painter.BeginPath();
painter.MoveTo(new Vector2(x * CellSize, 0));
painter.LineTo(new Vector2(x * CellSize, contentRect.height));
painter.Stroke();
}
for (var y = 1; y < yCount; y++)
{
painter.BeginPath();
painter.MoveTo(new Vector2(0, y * CellSize));
painter.LineTo(new Vector2(contentRect.width, y * CellSize));
painter.Stroke();
}
}
}
}

View File

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

View File

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
#if UNITY_5_3_OR_NEWER && UNITY_WINDOW
@ -11,11 +12,13 @@ using AnotherFileBrowser.Windows;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UIElements;
using Pointer = UnityEngine.InputSystem.Pointer;
namespace BITKit.UX
{
public class UXModService:UIToolKitPanel,IDisposable
public class UXModService:UIToolKitPanel
{
protected override string DocumentPath => "ux_mod_Service";
private const string TemplatePath = "ux_mod_service_template";
@ -37,8 +40,7 @@ namespace BITKit.UX
private Label _modDescriptionLabel;
[UXBindPath("reload-mod-button",true)]
private Button _reloadModButton;
[UXBindPath("install-roslyn-fill")]
private VisualElement _installRoslynFill;
private readonly ConcurrentDictionary<string,VisualElement> _modContainers=new();
public UXModService(IUXService uxService) : base(uxService)
@ -54,8 +56,6 @@ namespace BITKit.UX
private async UniTask InitializeAsync()
{
_installRoslynFill.style.width = 0;
_modTemplate =await ModService.LoadAsset<VisualTreeAsset>(TemplatePath);
UXUtils.Inject(this);
@ -134,6 +134,7 @@ namespace BITKit.UX
private VisualElement Create(IMod mod)
{
var container =_modsContainer.Create(_modTemplate);
container.Get<Toggle>().SetValueWithoutNotify(ModService.Mods.Contains(mod));
container.Get<Toggle>().RegisterValueChangedCallback(evt =>
{
if (evt.newValue)
@ -145,6 +146,7 @@ namespace BITKit.UX
ModService.UnLoad(mod);
}
});
container.Get<Label>().text = mod.Name +"@"+mod.PackageName;
container.tooltip = mod.Name+"\n"+mod.Description;
container.Get<Button>().clicked += () =>
@ -155,8 +157,9 @@ namespace BITKit.UX
return container;
}
public void Dispose()
public override void Dispose()
{
base.Dispose();
ModService.OnModInstalled-=OnModInstalled;
ModService.OnModUnInstalled-=OnModUnInstalled;
ModService.OnModLoaded-=OnModLoaded;

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f51cb0688f8e0454e8dce6e90da31939
guid: 64e4049b9c8014d43bb7431cb56b6a09
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -0,0 +1,111 @@
using System;
using System.Linq;
using BITKit.Mod;
using BITKit.UX.Hotkey;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class UXRadialMenu : UIToolKitPanel
{
protected override string DocumentPath => "ui_radial_menu";
public override bool CloseWhenClickOutside => true;
public override bool AllowCursor => true;
private VisualTreeAsset _template;
[UXBindPath("radialMenu-container")]
private VisualElement _container;
[UXBindPath("info-label")]
private Label _infoLabel;
public IHotkeyCollection HotkeyCollection { get; set; }
public UXRadialMenu(IUXService uxService) : base(uxService)
{
OnInitiatedAsync += InitiatedAsync;
}
private async UniTask InitiatedAsync()
{
_container.Clear();
_template =await ModService.LoadAsset<VisualTreeAsset>("ui_radial_menu-template");
RootVisualElement.RegisterCallback<PointerDownEvent>(x =>
{
UXService.Return();
});
}
protected override void OnPanelEntry()
{
base.OnPanelEntry();
_infoLabel.text = "选择快速动作";
if (HotkeyCollection is null)
{
_infoLabel.text = "<color=yellow>没有快速动作</color>";
return;
}
var count = HotkeyCollection.Hotkeys.Count();
if (count is 0)
{
_infoLabel.text = "<color=yellow>目前没有快速动作</color>";
}
for (var i = 0; i < count; i++)
{
var hotkey = HotkeyCollection.Hotkeys.ElementAt(i);
var angle = 360 / count * i;
var pos = Quaternion.Euler(0, 0, angle) * Vector3.up * 384;
pos.y *= 0.64f;
var container = _container.Create<VisualElement>(_template.CloneTree);
var button = container.Get<Button>();
button.text = hotkey.Name;
button.focusable = false;
button.clickable = hotkey.HoldDuration is 0 ? new Clickable(OnClick) : null;
container.style.position = Position.Absolute;
container.transform.position = pos;
container.SetEnabled(hotkey.Enabled);
container.RegisterCallback<PointerOverEvent>(OnMouseOver);
continue;
void OnClick()
{
if (!hotkey.Enabled) return;
if (hotkey.OnPerform is not null)
{
try
{
hotkey.OnPerform();
}
catch (Exception e)
{
Debug.LogException(e);
}
}
UXService.Return();
}
void OnMouseOver(PointerOverEvent evt)
{
_infoLabel.text = hotkey.Description;
}
}
}
protected override void OnPanelExit()
{
base.OnPanelExit();
_container.Clear();
}
}
}

View File

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

View File

@ -40,7 +40,6 @@ namespace BITKit.UX
uxService.Register(this);
InitializeAsync().Forget();
}
private async UniTask InitializeAsync()
{
await _isBusy;
@ -89,20 +88,20 @@ namespace BITKit.UX
UXUtils.Inject(this,RootVisualElement);
OnInitiated?.Invoke();
RootVisualElement.SetActive(false);
await OnInitiatedAsync.UniTaskFunc();
WaitUtilInitialized.TrySetResult();
RootVisualElement.RegisterCallback<TransitionRunEvent>(OnTransitionRun);
RootVisualElement.RegisterCallback<TransitionStartEvent>(OnTransitionStart);
RootVisualElement.RegisterCallback<TransitionStartEvent>(OnTransitionStart);
RootVisualElement.RegisterCallback<TransitionEndEvent>(OnTransitionEnd);
RootVisualElement.RegisterCallback<TransitionCancelEvent>(OnTransitionEnd);
WaitUtilTransitionCompleted.TrySetResult();
WaitUtilInitialized.TrySetResult();
OnInitiated?.Invoke();
}
}
@ -127,6 +126,7 @@ namespace BITKit.UX
public virtual bool AllowReload { get; }
public virtual bool AllowCursor { get; }
public virtual bool AllowInput { get; }
public object Root => RootVisualElement;
public virtual string[] InitialUssClasses { get; } = Array.Empty<string>();
public bool IsDisposed { get; private set; }
@ -152,6 +152,11 @@ namespace BITKit.UX
RootVisualElement.SetActive(true);
//await UniTask.NextFrame();
if (IsWindow)
{
RootVisualElement.BringToFront();
}
RootVisualElement.AddToClassList(USSEntry);
@ -174,9 +179,19 @@ namespace BITKit.UX
{
BIT4Log.LogException(e);
}
await WaitUtilTransitionCompleted.Task;
try
{
var cts = new CancellationTokenSource();
cts.CancelAfter(1000);
await WaitUtilTransitionCompleted.Task.AttachExternalCancellation(cts.Token);
}
catch (OperationCanceledException)
{
}
await UniTask.SwitchToMainThread();
RootVisualElement.AddToClassList(USSEntered);
}
private void OnTransitionEnd<TChangeEvent>(TChangeEvent evt)
@ -218,8 +233,17 @@ namespace BITKit.UX
await UniTask.NextFrame();
await OnExitAsync.UniTaskFunc();
try
{
var cts = new CancellationTokenSource();
cts.CancelAfter(1000);
await WaitUtilTransitionCompleted.Task.AttachExternalCancellation(cts.Token);
}
catch (OperationCanceledException)
{
}
await WaitUtilTransitionCompleted.Task;
}
void IEntryElement.Exited()
{
@ -249,7 +273,7 @@ namespace BITKit.UX
{
}
public void Dispose()
public virtual void Dispose()
{
RootVisualElement?.RemoveFromHierarchy();
IsDisposed = true;

View File

@ -0,0 +1,141 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class UIToolkitSubPanel<T> :IUXPanel where T : IUXPanel
{
private readonly IServiceProvider _serviceProvider;
protected readonly T Panel;
protected readonly ValidHandle IsVisible=new();
private readonly ValidHandle _busy = new();
public UIToolkitSubPanel(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
Panel = serviceProvider.GetRequiredService<T>();
Panel.OnInitiated += OnInitiated;
Panel.OnInitiatedAsync += OnInitiatedAsync;
if (Panel is UIToolKitPanel uiToolKitPanel)
{
if (uiToolKitPanel.WaitUtilInitialized.Task.Status is UniTaskStatus.Succeeded)
{
OnInitiatedAsync().Forget();
OnInitiated();
}
}
Panel.OnEntry +=()=> IsVisible.RemoveDisableElements(this);
Panel.OnExit +=()=> IsVisible.AddDisableElements(this);
IsVisible.AddListener(IsVisibleCallback);
}
private async void IsVisibleCallback(bool x)
{
await _busy;
using var busy = _busy.GetHandle();
if (x)
{
OnEntry?.Invoke();
OnEntryAsync?.Invoke();
OnEntryCompleted?.Invoke();
}
else
{
OnExit?.Invoke();
OnExitAsync?.Invoke();
OnExitCompleted?.Invoke();
}
}
protected virtual UniTask OnInitiatedAsync()
{
UXUtils.Inject(this,Panel.Root as VisualElement);
return UniTask.CompletedTask;
}
protected virtual void OnInitiated()
{ UXUtils.Inject(this,Panel.Root as VisualElement);
}
bool IEntryElement.IsEntered
{
get => Panel.IsEntered;
set => Panel.IsEntered = value;
}
void IEntryElement.Entry()
{
Panel.Entry();
}
UniTask IEntryElement.EntryAsync()
{
return Panel.EntryAsync();
}
void IEntryElement.Entered()
{
Panel.Entered();
}
void IEntryElement.Exit()
{
Panel.Exit();
}
UniTask IEntryElement.ExitAsync()
{
return Panel.ExitAsync();
}
void IEntryElement.Exited()
{
Panel.Exited();
}
bool IUXPanel.IsWindow => Panel.IsWindow;
string IUXPanel.Index => Panel.Index;
bool IUXPanel.AllowCursor => Panel.AllowCursor;
bool IUXPanel.AllowInput => Panel.AllowInput;
object IUXPanel.Root => Panel.Root;
public event Action OnEntry;
public event Func<UniTask> OnEntryAsync;
public event Action OnEntryCompleted;
public event Action OnExit;
public event Func<UniTask> OnExitAsync;
public event Action OnExitCompleted;
event Action IUXPanel.OnInitiated
{
add => Panel.OnInitiated += value;
remove => Panel.OnInitiated -= value;
}
event Func<UniTask> IUXPanel.OnInitiatedAsync
{
add => Panel.OnInitiatedAsync += value;
remove => Panel.OnInitiatedAsync -= value;
}
void IUXPanel.OnTick(float deltaTime)
{
Panel.OnTick(deltaTime);
}
}
}

View File

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

View File

@ -5,7 +5,10 @@ using System.Threading;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
@ -25,9 +28,26 @@ namespace BITKit.UX
_serviceProvider = serviceProvider;
_cancellationTokenSource = cancellationTokenSource;
_entryGroup.OnEntry += OnEntry;
_windowEntryGroup.OnEntry += OnWindowEntry;
_windowEntryGroup.OnExit += OnWindowExit;
_ticker.Add(OnTick);
}
private void OnWindowExit(IUXPanel obj)
{
BITAppForUnity.AllowCursor.RemoveElement(_windowEntryGroup);
if (obj.AllowInput is false)
BITInputSystem.AllowInput.RemoveDisableElements(_windowEntryGroup);
}
private void OnWindowEntry(IUXPanel obj)
{
BITAppForUnity.AllowCursor.SetElements(_windowEntryGroup, obj.AllowCursor);
if (obj.AllowInput is false)
BITInputSystem.AllowInput.AddDisableElements(_windowEntryGroup);
}
private readonly EntryGroup<IUXPanel> _entryGroup = new();
private readonly EntryGroup<IUXPanel> _windowEntryGroup = new();
/// <summary>
@ -73,9 +93,9 @@ namespace BITKit.UX
public string SettingsPath { get; set; } = "ux_panel_settings";
public object Root { get; private set; }
public static VisualElement RootVisualElement { get; private set; }
public async UniTask InitializeAsync()
{
var gameObject = new GameObject("UXService");
Object.DontDestroyOnLoad(gameObject);
@ -87,6 +107,11 @@ Object.Destroy(gameObject);
var document = gameObject.AddComponent<UIDocument>();
try
{
if (Touchscreen.current is not null && SettingsPath == "ux_panel_settings")
{
SettingsPath = "ux_panel_settings_mobile";
}
var panelSettings =await ModService.LoadAsset<PanelSettings>(SettingsPath);
document.panelSettings = panelSettings;
}
@ -96,8 +121,12 @@ Object.Destroy(gameObject);
throw;
}
Root = document.rootVisualElement;
Root = RootVisualElement= document.rootVisualElement;
if (Touchscreen.current is not null)
{
RootVisualElement.AddToClassList("mobile");
}
}
public void Register(IUXPanel panel) => _registryQueue.Enqueue(panel);
@ -115,6 +144,22 @@ Object.Destroy(gameObject);
public void Entry(string panelName) => _entryQueueByName.TryAdd(panelName);
public IUXPanel CurrentPanel => _currentPanel;
public event Action<IUXPanel, IUXPanel> OnPanelChanged;
public bool TryPick(float2 position, out object obj)
{
obj = null;
if (!EventSystem.current.IsPointerOverGameObject())
{
return false;
}
position.y = Screen.height - position.y;
var ve = RootVisualElement.panel.Pick(RuntimePanelUtils.ScreenToPanel(RootVisualElement.panel, position));
obj = ve;
return obj is not null;
}
public void Return()
{
@ -143,6 +188,7 @@ Object.Destroy(gameObject);
{
try
{
while (_registryQueue.TryDequeue(out var result))
{
if (result is null) continue;
@ -201,6 +247,13 @@ Object.Destroy(gameObject);
public async void Dispose()
{
foreach (var panelsValue in _panels.Values)
{
if (panelsValue is IDisposable disposable)
{
disposable.Dispose();
}
}
_ticker.Remove(OnTick);
await UniTask.SwitchToMainThread();
if (_currentPanel is not null)

View File

@ -3,6 +3,8 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
#if UNITY_EDITOR
@ -10,103 +12,106 @@ using Types = FullscreenEditor.Types;
#endif
namespace BITKit.UX.Settings
{
public class UXSettings : MonoBehaviour
public interface IUXSettings:IUXPanel{}
public class UXSettings<T,TValue> : UIToolkitSubPanel<T>,IUXSettings where T: IUXPanel
{
[SerializeField] private UIDocument document;
[UXBindPath("resolution-dropdown")]
private DropdownField _resolutionDropdown;
[UXBindPath("sensitivity-slider")]
private Slider _sensitivitySlider;
[UXBindPath("fullscreen-toggle")]
private Toggle _fullscreenToggle;
private void Start()
private readonly TValue _value;
private readonly IWrapper<TValue> _valueWrapper;
public UXSettings(IServiceProvider serviceProvider, IWrapper<TValue> valueWrapper) : base(serviceProvider)
{
UXUtils.Inject(this);
_resolutionDropdown.choices = Screen.resolutions.Select(x => x.ToString()).ToList();
_resolutionDropdown.SetValueWithoutNotify(Screen.currentResolution.ToString());
_resolutionDropdown.RegisterValueChangedCallback(x =>
{
Data.Set(Constant.Environment.mat_setvideomode, x.newValue);
});
_fullscreenToggle.SetValueWithoutNotify(Screen.fullScreen);
_fullscreenToggle.RegisterValueChangedCallback(x =>
{
Data.Set(Constant.Environment.fullscreen, x.newValue);
});
//_sensitivitySlider.SetValueWithoutNotify(PlayerConfig.Singleton.Sensitivity);
_sensitivitySlider.RegisterValueChangedCallback(x =>
{
Data.Set(Constant.Environment.sensitivity, x.newValue);
});
Data.AddListener<string>(Constant.Environment.mat_setvideomode, OnResolutionChanged);
Data.AddListener<bool>(Constant.Environment.fullscreen,OnFullScreenChanged);
Data.AddListener<float>(Constant.Environment.sensitivity,OnSensitivityChanged);
destroyCancellationToken.Register(() =>
{
Data.RemoveListender<string>(Constant.Environment.mat_setvideomode, OnResolutionChanged);
Data.RemoveListender<bool>(Constant.Environment.fullscreen,OnFullScreenChanged);
Data.RemoveListender<float>(Constant.Environment.sensitivity,OnSensitivityChanged);
PlayerPrefs.Save();
});
if(PlayerPrefs.HasKey(Constant.Environment.fullscreen))
Data.Set(Constant.Environment.fullscreen, PlayerPrefs.GetInt(Constant.Environment.fullscreen) == 1);
if(PlayerPrefs.HasKey(Constant.Environment.sensitivity))
Data.Set(Constant.Environment.sensitivity, PlayerPrefs.GetFloat(Constant.Environment.sensitivity));
_valueWrapper = valueWrapper;
_value = _valueWrapper.Value;
}
private async void OnSensitivityChanged(float obj)
{
await UniTask.SwitchToMainThread(destroyCancellationToken);
_sensitivitySlider.SetValueWithoutNotify(obj);
PlayerPrefs.SetFloat(Constant.Environment.sensitivity,obj);
}
private async void OnFullScreenChanged(bool obj)
{
await UniTask.SwitchToMainThread(destroyCancellationToken);
_fullscreenToggle.SetValueWithoutNotify(obj);
[UXBindPath("settings-container")]
private VisualElement _settingsContainer;
#if UNITY_EDITOR
if (obj)
private void Save()
{
_valueWrapper.Value = _value;
}
protected override void OnInitiated()
{
base.OnInitiated();
_settingsContainer.Clear();
foreach (var propertyInfo in typeof(TValue).GetProperties())
{
FullscreenEditor.Fullscreen.MakeFullscreen(Types.GameView);
}
else
{
foreach (var x in FullscreenEditor.Fullscreen.GetAllFullscreen())
var name = propertyInfo.GetDisplayName();
var value = propertyInfo.GetValue(_valueWrapper.Value);
switch (propertyInfo.PropertyType)
{
x.Close();
case var type when type == typeof(bool):
{
var checkBox = _settingsContainer.Create<Toggle>();
checkBox.label = name;
checkBox.SetValueWithoutNotify(value.As<bool>());
checkBox.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
case var type when type == typeof(float):
{
var slider = _settingsContainer.Create<Slider>();
slider.label = name;
slider.showInputField = true;
slider.showMixedValue = true;
slider.lowValue = 1;
slider.highValue = 100;
slider.SetValueWithoutNotify(value.As<float>());
slider.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
case var type when type == typeof(int):
{
var slider = _settingsContainer.Create<SliderInt>();
slider.label = name;
slider.showInputField = true;
slider.showMixedValue = true;
slider.lowValue = 1;
slider.highValue = 100;
slider.SetValueWithoutNotify(value.As<int>());
slider.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
case var type when type == typeof(string):
{
var textField = _settingsContainer.Create<TextField>();
textField.label = name;
textField.SetValueWithoutNotify(value.As<string>());
textField.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
case var type when type.IsEnum:
{
var enumField = _settingsContainer.Create<EnumField>();
enumField.label = name;
enumField.Init(value as Enum);
enumField.SetValueWithoutNotify(value as Enum);
enumField.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
}
}
#else
Screen.fullScreen = obj;
#endif
PlayerPrefs.SetInt(Constant.Environment.fullscreen, obj ? 1 : 0);
}
private async void OnResolutionChanged(string newResolution)
{
await UniTask.SwitchToMainThread(destroyCancellationToken);
_resolutionDropdown.SetValueWithoutNotify(newResolution);
var resolution = Screen.resolutions.Single(x => x.ToString() == newResolution);
Screen.SetResolution(resolution.width,resolution.height,Data.Get<bool>(Constant.Environment.fullscreen));
}
}

View File

@ -84,7 +84,18 @@ namespace BITKit.UX
return "";
}
var screenPosition = Mouse.current.position.ReadValue();
var screenPosition = Vector2.zero;
if (Mouse.current is not null)
{
screenPosition= Mouse.current.position.ReadValue();
}
if (Touchscreen.current is not null)
{
screenPosition = Touchscreen.current.position.ReadValue();
}
screenPosition.y = Screen.height - screenPosition.y;
VisualElement ve = panel.Pick(RuntimePanelUtils.ScreenToPanel(panel, screenPosition));

View File

@ -204,8 +204,8 @@ namespace BITKit
} while (property.NextVisible(false));
foreach (var method in serializedObject.targetObject.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (method.GetCustomAttribute<BITAttribute>() is null) continue;
{
if (method.GetCustomAttribute<BITAttribute>() is null&& method.GetCustomAttribute<ContextMenu>() is null) continue;
if (method.GetParameters().Length is not 0) continue;
var button = new Button(() =>
{

View File

@ -11,6 +11,8 @@ using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using System.Text;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
namespace BITKit
{
public static partial class MathQ
@ -34,6 +36,22 @@ namespace BITKit
}
public static partial class MathV
{
public static Matrix4x4 Rotate(Transform self, Vector3 center, Vector3 euler)
{
// 计算物体与中心点的相对位置
Vector3 relativePosition = self.position - center;
// 使用欧拉角来计算旋转矩阵
Quaternion rotation = Quaternion.Euler(euler);
// 计算旋转后的相对位置
Vector3 rotatedPosition = rotation * relativePosition;
var newPosition = center + rotatedPosition;
var newRotation = rotation * self.rotation;
return Matrix4x4.TRS(newPosition, newRotation, Vector3.one);
}
public static Vector3 CalculateTorque(Transform transform,Quaternion targetRotation)
{
var deltaRot = targetRotation * Quaternion.Inverse(transform.rotation);
@ -157,6 +175,12 @@ namespace BITKit
return direction.sqrMagnitude < factor; */
return Vector3.Angle(vectorX, vectorY) < 8;
}
public static Vector2 TransientRotationAxis(Vector2 transientAxis)
{
transientAxis.x = TransientRotationAxis(transientAxis.x);
transientAxis.y = TransientRotationAxis(transientAxis.y);
return transientAxis;
}
public static Vector3 TransientRotationAxis(Vector3 transientAxis)
{
transientAxis.x = TransientRotationAxis(transientAxis.x);
@ -218,18 +242,18 @@ namespace BITKit
}
public static float TransientRotationAxis(this float transientAxis)
{
return (transientAxis %= 360) switch
return (transientAxis %= 360f) switch
{
> 180 => transientAxis - 360,
< -180 => transientAxis + 360,
> 180f => transientAxis - 360f,
< -180f => transientAxis + 360f,
_ => transientAxis,
};
}
public static float WrapAngle(float angle)
{
angle %= 360;
if (angle > 180)
return angle - 360;
angle %= 360f;
if (angle > 180f)
return angle - 360f;
return angle;
}
@ -455,16 +479,23 @@ namespace BITKit
self.TryGetValue(key, out value);
return value; ;
}
public static void DestoryChilds(this GameObject self)
public static void DestoryChilds(this GameObject self,bool immediately=false)
{
if (self)
if (!self) return;
var list = self.GetComponentsInChildren<Transform>().ToList();
list.TryRemove(self.transform);
foreach (var x in list)
{
var list = self.GetComponentsInChildren<Transform>().ToList();
list.TryRemove(self.transform);
list.ForEach(x =>
if (immediately)
{
GameObject.Destroy(x.gameObject);
});
Object.DestroyImmediate(x.gameObject);
}
else
{
Object.Destroy(x.gameObject);
}
}
}
public static bool Contains(this string self, IEnumerable<string> strs, out string key)

View File

@ -1,22 +0,0 @@
{
"name": "BITKit.WorldNode.Runtime",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:508392158bd966c4d9c21e19661a441d",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:1193c2664d97cc049a6e4c486c6bce71",
"GUID:d750d221812bb1d48baff92e6ef73e28"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -136,4 +136,3 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

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

View File

@ -1,16 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.GameSocket
{
public interface ISocketComponent
{
public string Name { get; }
public object Object { get; }
}
public interface ISocketService:IReadOnlyDictionary<string,ISocketComponent>
{
void Initialize();
}
}

View File

@ -1,14 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.GameSocket
{
public class UnitySocketComponent : MonoBehaviour,ISocketComponent
{
[SerializeReference,SubclassSelector] private IReference nameReference;
public string Name => nameReference.Value;
public object Object => transform;
}
}

View File

@ -1,50 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using AYellowpaper.SerializedCollections;
using UnityEngine;
namespace BITKit.GameSocket
{
public class UnitySocketService : MonoBehaviour,ISocketService
{
private readonly Dictionary<string, ISocketComponent> _sockets=new();
public void Initialize()
{
_sockets.Clear();
foreach (var x in GetComponentsInChildren<ISocketComponent>())
{
_sockets.Add(x.Name, x);
}
}
public IEnumerator<KeyValuePair<string, ISocketComponent>> GetEnumerator()
{
return _sockets.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _sockets.GetEnumerator();
}
public int Count => _sockets.Count;
public bool ContainsKey(string key)
{
return _sockets.ContainsKey(key);
}
public bool TryGetValue(string key, out ISocketComponent value)
{
return _sockets.TryGetValue(key, out value);
}
public ISocketComponent this[string key] => _sockets[key];
public IEnumerable<string> Keys => _sockets.Keys;
public IEnumerable<ISocketComponent> Values => _sockets.Values;
}
}

View File

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

View File

@ -526,3 +526,122 @@ TabContainer > * {
opacity: 0;
transition-duration: 0.1s;
}
OnScreenButton {
width: 80px;
height: 80px;
border-top-left-radius: 64px;
border-top-right-radius: 64px;
border-bottom-right-radius: 64px;
border-bottom-left-radius: 64px;
-unity-background-image-tint-color: rgb(255, 255, 255);
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-color: rgb(255, 255, 255);
border-left-color: rgb(255, 255, 255);
border-right-color: rgb(255, 255, 255);
border-bottom-color: rgb(255, 255, 255);
}
OnScreenButton:hover {
background-color: rgba(200, 200, 200, 0.5);
-unity-background-image-tint-color: rgb(255, 255, 255);
}
OnScreenButton:active {
background-color: rgba(0, 0, 0, 0.5);
-unity-background-image-tint-color: rgb(255, 255, 255);
}
OnScreenButton.selected {
border-left-color: rgb(255, 0, 0);
border-right-color: rgb(255, 0, 0);
border-top-color: rgb(255, 0, 0);
border-bottom-color: rgb(255, 0, 0);
border-top-width: 4px;
border-right-width: 4px;
border-bottom-width: 4px;
border-left-width: 4px;
opacity: 0.5;
}
.gamepad-button {
border-left-color: rgb(255, 255, 255);
border-right-color: rgb(255, 255, 255);
border-top-color: rgb(255, 255, 255);
border-bottom-color: rgb(255, 255, 255);
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
margin-top: 4px;
margin-right: 4px;
margin-bottom: 4px;
margin-left: 4px;
}
.gamepad-button:hover {
border-left-color: rgb(255, 255, 255);
border-right-color: rgb(255, 255, 255);
border-top-color: rgb(255, 255, 255);
border-bottom-color: rgb(255, 255, 255);
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
background-color: rgba(255, 255, 255, 0.25);
}
.gamepad-button:active {
background-color: rgba(255, 255, 255, 0.5);
-unity-background-image-tint-color: rgba(0, 0, 0, 0.5);
border-left-color: rgba(0, 0, 0, 0.5);
border-right-color: rgba(0, 0, 0, 0.5);
border-top-color: rgba(0, 0, 0, 0.5);
border-bottom-color: rgba(0, 0, 0, 0.5);
}
.gamepad-button--nw {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-up-left-rounded.png?fileID=21300000&guid=e2a7dc01d1a854b429e7698649c340bd&type=3#icon_keyboard-arrow-up-left-rounded");
transform-origin: right bottom;
scale: 0.8 0.8;
}
.gamepad-button--n {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-up-rounded.png?fileID=21300000&guid=f75638302ccf8ba41acbf96be794e86d&type=3#icon_keyboard-arrow-up-rounded");
}
.gamepad-button--ne {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-up-right-rounded.png?fileID=21300000&guid=08e2edf7cdff08240a792ce62c67014d&type=3#icon_keyboard-arrow-up-right-rounded");
transform-origin: left bottom;
scale: 0.8 0.8;
}
.gamepad-button--w {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_chevron-left-rounded.png?fileID=21300000&guid=76da1f1cbb542714283e16db9aa8af7c&type=3#icon_chevron-left-rounded");
}
.gamepad-button--c {
}
.gamepad-button--e {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_chevron-right-rounded.png?fileID=21300000&guid=9e83d4b468bd75a47ab3d72155f367e5&type=3#icon_chevron-right-rounded");
}
.gamepad-button--sw {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-down-left-rounded.png?fileID=21300000&guid=93b327f2e8bb65f40a1b47d13ecfafcf&type=3#icon_keyboard-arrow-down-left-rounded");
transform-origin: right top;
scale: 0.8 0.8;
}
.gamepad-button--s {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/icon_keyboard-arrow-down.png?fileID=21300000&guid=6f14c0574452bf543b5f2f2cd2a24104&type=3#icon_keyboard-arrow-down");
}
.gamepad-button--se {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-down-right-rounded.png?fileID=21300000&guid=6103653931b39ca41a64af379712495c&type=3#icon_keyboard-arrow-down-right-rounded");
transform-origin: left top;
scale: 0.8 0.8;
}

View File

@ -13,13 +13,9 @@ MonoBehaviour:
m_Name: Common_Mobile
m_EditorClassIdentifier:
themeUss: {fileID: -4733365628477956816, guid: ac803f0924e239645ac6b2fc9baebc65, type: 3}
m_DisableNoThemeWarning: 0
m_TargetTexture: {fileID: 0}
m_RenderMode: 0
m_WorldSpaceLayer: 0
m_ScaleMode: 2
m_ReferenceSpritePixelsPerUnit: 100
m_PixelsPerUnit: 100
m_Scale: 1
m_ReferenceDpi: 96
m_FallbackDpi: 96
@ -28,11 +24,9 @@ MonoBehaviour:
m_Match: 1
m_SortingOrder: 0
m_TargetDisplay: 0
m_BindingLogLevel: 0
m_ClearDepthStencil: 1
m_ClearColor: 0
m_ColorClearValue: {r: 0, g: 0, b: 0, a: 0}
m_VertexBudget: 0
m_DynamicAtlasSettings:
m_MinAtlasSize: 8
m_MaxAtlasSize: 4096
@ -41,6 +35,4 @@ MonoBehaviour:
m_AtlasBlitShader: {fileID: 9101, guid: 0000000000000000f000000000000000, type: 0}
m_RuntimeShader: {fileID: 9100, guid: 0000000000000000f000000000000000, type: 0}
m_RuntimeWorldShader: {fileID: 9102, guid: 0000000000000000f000000000000000, type: 0}
m_ICUDataAsset: {fileID: 0}
forceGammaRendering: 0
textSettings: {fileID: 0}

View File

@ -12,8 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 19101, guid: 0000000000000000e000000000000000, type: 0}
m_Name: Common_PC_Overlay
m_EditorClassIdentifier:
themeUss: {fileID: -4733365628477956816, guid: ac803f0924e239645ac6b2fc9baebc65,
type: 3}
themeUss: {fileID: -4733365628477956816, guid: ac803f0924e239645ac6b2fc9baebc65, type: 3}
m_TargetTexture: {fileID: 0}
m_ScaleMode: 2
m_ReferenceSpritePixelsPerUnit: 100

View File

@ -1,10 +0,0 @@
.rank {
}
.D1 .rank {
background-image: url("project://database/Assets/Artists/UX/USS_1/UI/UI_Settlement/%E7%AC%AC1%E5%90%8D.png?fileID=2800000&guid=24cbc98f27967534189bd6172d815874&type=3#第1名");
}
.D2 .rank {
background-image: url("project://database/Assets/Artists/UX/USS_1/UI/UI_Settlement/%E7%AC%AC2%E5%90%8D.png?fileID=2800000&guid=75f797e60a65f654cbfece5f451ec695&type=3#第2名");
}

Some files were not shown because too many files have changed in this diff Show More