This commit is contained in:
parent
41715e4413
commit
8261a458e2
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6d0be10870bf27f4b8b1c97ed8809308
|
||||
guid: 66cea81a8a712d348aded0734f12d2ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit.Entities
|
||||
{
|
||||
public class LocalPlayerComponent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b030d336e53d2c646a7b382eb83897bf
|
||||
guid: 46172bbdf3a15d4458646b3e80b6702c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ namespace BITKit.Entities
|
|||
/// </summary>
|
||||
public interface IEntitiesService
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 当添加Entity时
|
||||
/// </summary>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2e822c111559c204ba6fc483b283d193
|
||||
guid: ad41977c90d905e4fa0efdcfd3260ccc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "BITKit.Hotkey",
|
||||
"rootNamespace": "",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": true
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 01cf845b40a364e4cbaee058936fa4a3
|
||||
guid: 3abaaefa7af558d44ba20cea1c43d602
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7ba6497a10fb8e14dab1acb39b2ec8aa
|
||||
guid: 0ad36bc56dd5406418bf8ea67f4bc010
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -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>
|
||||
/// 价值
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()); // 加载程序集
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c9d7136f598a780459b866ee63545a5f
|
||||
guid: f2fba06a66e155a4580df30630f030fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"reference": "GUID:14fe60d984bf9f84eac55c6ea033a8f4"
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2da90b0890b24e644a1900960dc2fb9c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
guid: 28ed10548f6aa6944ab6ac01ce4f9faf
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 16ca25bd5fef10847aec8081266207e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"reference": "GUID:14fe60d984bf9f84eac55c6ea033a8f4"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d31ceb431a775db4a8757a1208d82106
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e1ff18f234ca249459fb3df556f90b11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6b2748010e8669a44b12cd163ea95fa2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,8 @@
|
|||
"name": "BITKit.Physics",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4"
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:6babdba9f8b742f40904649736008000"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ae14ae5985cea8e40946355bfdf7113b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e856e0f7ca7746740a416303f76dc89d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 979c184b4aaba6a439cbe74f0196267f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
|
|
|
@ -11,7 +11,7 @@ using UnityEngine;
|
|||
using UnityEngine.Jobs;
|
||||
using UnityEngine.Pool;
|
||||
|
||||
namespace BITFALL.Rig{
|
||||
namespace BITKit.UX.Rig{
|
||||
public class TickOverrideTranformService : MonoBehaviour
|
||||
{
|
||||
//[BurstCompile]
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BITFALL.Rig
|
||||
namespace BITKit.UX.Rig
|
||||
{
|
||||
public class TickOverrideTransform : MonoBehaviour
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
"GUID:517785bb4600a5140b47eac5fa49b8fc",
|
||||
"GUID:838d3286f0973344ab6e99d3951012f7",
|
||||
"GUID:a11ff146d38b27a44af87b4b4d9c4ecb",
|
||||
"GUID:e4d11af1289097a4d9d8987f332a2ae8"
|
||||
"GUID:e4d11af1289097a4d9d8987f332a2ae8",
|
||||
"GUID:3abaaefa7af558d44ba20cea1c43d602",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9d540311d69835747a74085c9e0aba6c
|
||||
guid: ea593ccd503f23741b162c0f5a00fb52
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -8,7 +8,8 @@
|
|||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:d525ad6bd40672747bde77962f1c401e",
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
|
||||
"GUID:517785bb4600a5140b47eac5fa49b8fc"
|
||||
"GUID:517785bb4600a5140b47eac5fa49b8fc",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8afc05714f63ce542a2e2ead78933966
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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>"<Gamepad>/buttonSouth"</c>
|
||||
/// and another uses <c>"<Gamepad>/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.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8d6822f32ddff3b4f9fd962009912a81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4bf58213910164e4d8cab38b29830a7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e068da31183a7244198cbd23b00ebdbf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7b5ba7b7494ea5d42b820dee5a7619c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f51cb0688f8e0454e8dce6e90da31939
|
||||
guid: 64e4049b9c8014d43bb7431cb56b6a09
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 961781fb0b2f7004dbb5f7956301c4fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3b6d7b7e48316f04f9130c5301091ee7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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(() =>
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 300c66b95bcadb345ba713cc787c90de
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8284a08494909e147b79d71c7142ed12
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue