This commit is contained in:
CortexCore 2024-04-16 04:15:06 +08:00
parent b673a9438d
commit 0362b2c606
183 changed files with 5695 additions and 1453 deletions

View File

@ -103,7 +103,7 @@ MonoBehaviour:
m_PrefilterSSAOSampleCountHigh: 1
m_PrefilterDBufferMRT1: 1
m_PrefilterDBufferMRT2: 1
m_PrefilterDBufferMRT3: 1
m_PrefilterDBufferMRT3: 0
m_PrefilterSoftShadowsQualityLow: 1
m_PrefilterSoftShadowsQualityMedium: 1
m_PrefilterSoftShadowsQualityHigh: 1

View File

@ -9775,6 +9775,7 @@ Transform:
- {fileID: 324953849}
- {fileID: 495300441}
- {fileID: 2087774128}
- {fileID: 554909162}
- {fileID: 1237214210}
- {fileID: 1228132877}
- {fileID: 676926185}
@ -11200,6 +11201,69 @@ Transform:
m_CorrespondingSourceObject: {fileID: 1093686136660250980, guid: d25e6ff943432834c877c29af5e8ff40, type: 3}
m_PrefabInstance: {fileID: 541966726}
m_PrefabAsset: {fileID: 0}
--- !u!1 &554909161
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 554909162}
- component: {fileID: 554909164}
- component: {fileID: 554909163}
m_Layer: 0
m_Name: Waiting
m_TagString: Untagged
m_Icon: {fileID: 2800000, guid: 2427f62ec555df341837da126e892853, type: 3}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &554909162
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 554909161}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 475671028}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &554909163
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 554909161}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 241915c4814e5224f9d3ffd6dab7a98e, type: 3}
m_Name:
m_EditorClassIdentifier:
handleTemplate: {fileID: 9197481963319205126, guid: 418bc306b2e063847b56f7b5f5a23f7d, type: 3}
asGlobal: 1
--- !u!114 &554909164
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 554909161}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_PanelSettings: {fileID: 11400000, guid: 64cd93f02c042ad43a96d66da32f0c6c, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: fd7776bbc384e3747a370568288ad98c, type: 3}
m_SortingOrder: 8
--- !u!1001 &555911125
PrefabInstance:
m_ObjectHideFlags: 0
@ -12219,6 +12283,109 @@ Transform:
m_CorrespondingSourceObject: {fileID: 2723349985642794010, guid: d22a44843d244fc4fb4459be68ead0bb, type: 3}
m_PrefabInstance: {fileID: 596252691}
m_PrefabAsset: {fileID: 0}
--- !u!1 &599164994
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 599164995}
- component: {fileID: 599164997}
- component: {fileID: 599164996}
m_Layer: 0
m_Name: manual-button
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &599164995
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 599164994}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1237214210}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &599164996
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 599164994}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d7c307dd96b0ae44db43b135d73ae46a, type: 3}
m_Name:
m_EditorClassIdentifier:
mark:
actions:
- rid: 1308798704739418115
references:
version: 2
RefIds:
- rid: -2
type: {class: , ns: , asm: }
- rid: 1308798704739418115
type: {class: BITAppForUnity/OpenUrl, ns: BITKit, asm: BITKit}
data:
url: http://server.bitfall.icu:3000/root/iFactory.Cutting.Unity
urlReference:
rid: -2
--- !u!114 &599164997
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 599164994}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 44e9dc9ae2389a74abe198bbd3f86c69, type: 3}
m_Name:
m_EditorClassIdentifier:
document: {fileID: 1237214209}
bindName:
bindNameProvider:
rid: 1308798704739418114
allowRightClick: 0
onClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 599164996}
m_TargetAssemblyTypeName: BITKit.MonoAction, BITKit
m_MethodName: Execute
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
onRightClick:
m_PersistentCalls:
m_Calls: []
clicked:
Targets: []
references:
version: 2
RefIds:
- rid: 1308798704739418114
type: {class: GetNameFromGameobject, ns: BITKit, asm: BITKit}
data:
gameobject: {fileID: 599164994}
--- !u!1001 &599941887
PrefabInstance:
m_ObjectHideFlags: 0
@ -24689,6 +24856,7 @@ Transform:
- {fileID: 128062587}
- {fileID: 857627674}
- {fileID: 698951664}
- {fileID: 599164995}
- {fileID: 1460596583}
m_Father: {fileID: 475671028}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using AYellowpaper.SerializedCollections;
@ -120,85 +121,7 @@ namespace BITFactory.Cutting
_brushFieldsContainer.Clear();
var fields = arg2.GetType().GetFields(ReflectionHelper.Flags);
foreach (var fieldInfo in fields)
{
if (Attribute.IsDefined(fieldInfo, typeof(ExportAttribute)) is false) continue;
var exportAttribute = fieldInfo.GetCustomAttribute<ExportAttribute>();
// var runtimeFieldInfo = arg2.GetType().GetRuntimeField(fieldInfo.Name);
// runtimeFieldInfo= arg2.GetType().GetField(fieldInfo.Name);
//
// Debug.Log(runtimeFieldInfo);
// var _ref = TypedReference.MakeTypedReference(arg2, new []{runtimeFieldInfo});
var handler = UXDataBindingService.CreateBinding(arg2.GetExport(fieldInfo.Name),fieldInfo.GetValue(arg2));
//var handler = UXDataBindingService.CreateBinding(_ref);
_brushFieldsContainer.Add(handler.visualElement);
handler.visualElement.GetType().GetProperty("label")!.SetValue(handler.visualElement,
string.IsNullOrEmpty(exportAttribute.Name) ? fieldInfo.Name:exportAttribute.Name
);
//var handler = UXDataBindingService.CreateBinding(ref arg2.GetExport(fieldInfo.Name));
// switch (fieldInfo.GetValue(arg2))
// {
// case float _float:
// var floatField = _brushFieldsContainer.Create<FloatField>();
// floatField.label =string.IsNullOrEmpty(exportAttribute.Name) ? fieldInfo.Name:exportAttribute.Name;
// floatField.value = _float;
// floatField.RegisterValueChangedCallback(evt =>
// {
// fieldInfo.SetValue(arg2, evt.newValue);
// });
// break;
// case Vector3 _vector3:
// var vector3Field = _brushFieldsContainer.Create<Vector3Field>();
// vector3Field.label =string.IsNullOrEmpty(exportAttribute.Name) ? fieldInfo.Name:exportAttribute.Name;
// vector3Field.value = _vector3;
// vector3Field.RegisterValueChangedCallback(evt =>
// {
// fieldInfo.SetValue(arg2, evt.newValue);
// });
// break;
// }
}
foreach (var methodInfo in arg2.GetType().GetMethods(ReflectionHelper.Flags))
{
if (Attribute.IsDefined(methodInfo, typeof(ExportAttribute)) is false) continue;
var exportAttribute = methodInfo.GetCustomAttribute<ExportAttribute>();
var button = _brushFieldsContainer.Create<Button>();
button.text =string.IsNullOrEmpty(exportAttribute.Name) ? methodInfo.Name:exportAttribute.Name;
button.clicked += OnClicked;
return;
void OnClicked()
{
try
{
methodInfo.Invoke(arg2, null);
}
catch (TargetInvocationException targetInvocationException)
{
if (targetInvocationException.InnerException is InGameException e is false) return;
switch (e)
{
case {InnerException:not null}:
Alert.Print(e.Message,e.InnerException.Message);
break;
default:
Alert.Print(e.Message,e.Source);
break;
}
}
}
}
_brushFieldsContainer.Add(UXDataBindingService.Create(arg2));
}
}
}

View File

@ -1,10 +1,13 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BITKit;
using BITKit.StateMachine;
using Newtonsoft.Json;
using PaintIn3D;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
@ -26,14 +29,9 @@ namespace BITFactory.Cutting
public override string Name => "点切削";
public override string Description => "按住鼠标左键进行画笔切削";
[BITKit.Export("切削半径")]
[Export("切削半径")]
public float Radius = 0;
public override unsafe void* GetExport(string name)
{
return name == nameof(Radius) ? UnsafeUtility.AddressOf(ref Radius) : base.GetExport(name);
}
public override void HandlePoint(bool isPreview, float3 normal, float3 point)
{
if (!isPreview) return;
@ -84,15 +82,6 @@ namespace BITFactory.Cutting
private float3 _normal=Vector3.up;
private readonly IntervalUpdate _interrupt = new(0.1f);
public override unsafe void* GetExport(string name)
{
return name switch
{
nameof(snapDifference)=>UnsafeUtility.AddressOf(ref snapDifference),
_ => base.GetExport(name)
};
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
@ -183,7 +172,21 @@ namespace BITFactory.Cutting
{
[SerializeField] private Transform transform;
public override string Name => "填充切削";
public override string Description => "仅在该程序中使用,用于预览被裁剪的区域";
public override string Description => "仅在该程序中使用,用于预览被裁剪的区域\n事实上现在用于测试运行时序列化字段";
[Export(name:"字符串列表")] private List<string> stringList = new();
[Export(name:"单浮点列表")] private List<float> floatList = new();
[Export(name:"整数列表")] private List<int> intList = new();
[Export(name:"字符串数组")] private string[] stringArray = Array.Empty<string>();
[Export(name:"单浮点数组")] private float[] floatArray = Array.Empty<float>();
[Export(name:"整数数组")] private int[] intArray = Array.Empty<int>();
[Export(name:"float3数组")] private float3 position;
public override void ReleasePoint(float3 normal, float3 point)
{
base.ReleasePoint(normal, point);
stringList.Add(JsonConvert.SerializeObject((Vector3)point));
}
[Export(name:"自动填充")]
private void AutoFill()

View File

@ -19,16 +19,6 @@ namespace BITFactory.Cutting
[Export("位置")] private Vector3 point;
[Export("半径")] private float radius = 0.01f;
public override unsafe void* GetExport(string name)
{
var x = __makeref(radius);
return name switch
{
nameof(radius) => UnsafeUtility.AddressOf(ref radius),
nameof(point) => UnsafeUtility.AddressOf(ref point),
_ => base.GetExport(name)
};
}
[Export("确认")]
private void Release()

View File

@ -13,7 +13,8 @@
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:15fc0a57446b3144c949da3e2b9737a9"
"GUID:15fc0a57446b3144c949da3e2b9737a9",
"GUID:e0cd26848372d4e5c891c569017e11f1"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -37,22 +37,31 @@
</ui:VisualElement>
<ui:Label tabindex="-1" text="笔刷" parse-escape-sequences="true" display-tooltip-when-elided="true" class="tl" style="display: none;" />
<ui:VisualElement picking-mode="Ignore" style="flex-direction: row;">
<ui:VisualElement name="cuttingBrush-container">
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" class="unity-disabled" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
</ui:VisualElement>
<ui:VisualElement picking-mode="Ignore" style="margin-right: 16px; margin-left: 8px;">
<ui:Label tabindex="-1" text="切削笔刷&#10;用于各种切割" parse-escape-sequences="true" display-tooltip-when-elided="true" name="brush-info-label" />
<ui:Label tabindex="-1" text="笔刷参数" parse-escape-sequences="true" display-tooltip-when-elided="true" class="tl" />
<ui:VisualElement name="brush-fields-container">
<ui:TextField picking-mode="Ignore" label="Text Field" value="filler text" />
<ui:Hash128Field label="Hash128 Field" value="885eea9fcca73bdadde4ecd6a3af1312" />
<ui:ScrollView>
<ui:VisualElement name="cuttingBrush-container">
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" class="unity-disabled" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
<ui:Instance template="CuttingBrush" name="CuttingBrush" />
</ui:VisualElement>
</ui:VisualElement>
</ui:ScrollView>
<ui:ScrollView elasticity="1" style="max-width: 512px; max-height: 384px;">
<ui:VisualElement picking-mode="Ignore" style="margin-right: 16px; margin-left: 8px;">
<ui:Label tabindex="-1" text="切削笔刷&#10;用于各种切割" parse-escape-sequences="true" display-tooltip-when-elided="true" name="brush-info-label" />
<ui:Label tabindex="-1" text="笔刷参数" parse-escape-sequences="true" display-tooltip-when-elided="true" class="tl" />
<ui:VisualElement name="brush-fields-container">
<ui:VisualElement style="flex-direction: row;">
<ui:TextField picking-mode="Ignore" label="Text Field" value="filler text" />
<ui:Button parse-escape-sequences="true" display-tooltip-when-elided="true" class="icon-remove" />
</ui:VisualElement>
<ui:TextField picking-mode="Ignore" label="Text Field" value="filler text" />
<ui:TextField picking-mode="Ignore" label="Text Field" value="filler text" class="error" />
<ui:Hash128Field label="Hash128 Field" value="885eea9fcca73bdadde4ecd6a3af1312" />
</ui:VisualElement>
</ui:VisualElement>
</ui:ScrollView>
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
@ -76,6 +85,7 @@
<ui:Button text="创意工坊" parse-escape-sequences="true" display-tooltip-when-elided="true" name="workshop-button" />
<ui:Foldout text="应用设置" class="--button">
<ui:Button text="设置" parse-escape-sequences="true" display-tooltip-when-elided="true" name="options-button" />
<ui:Button text="用户手册" parse-escape-sequences="true" display-tooltip-when-elided="true" name="manual-button" />
<ui:Button text="退出" parse-escape-sequences="true" display-tooltip-when-elided="true" name="exit-button" />
</ui:Foldout>
<ui:VisualElement style="flex-direction: row; align-items: center;">
@ -104,7 +114,7 @@
</ui:VisualElement>
<ui:VisualElement>
<ui:Label tabindex="-1" text="切削命令历史" parse-escape-sequences="true" display-tooltip-when-elided="true" name="Label" class="tl" />
<ui:ScrollView>
<ui:ScrollView elasticity="1">
<ui:VisualElement name="commands-container">
<ui:Button text="切割:1" parse-escape-sequences="true" display-tooltip-when-elided="true" />
<ui:Button text="切割:1" parse-escape-sequences="true" display-tooltip-when-elided="true" />

View File

@ -10,6 +10,17 @@ namespace BITKit
[AttributeUsage(AttributeTargets.Field)]
public class InjectAttribute : System.Attribute
{
public static void Clear(object obj)
{
var fields = obj.GetType().GetFields(ReflectionHelper.Flags);
foreach (var field in fields)
{
if (field.GetCustomAttributes(typeof(InjectAttribute), true).Length > 0)
{
field.SetValue(obj, null);
}
}
}
public readonly bool CanBeNull;
public InjectAttribute()
{
@ -39,8 +50,7 @@ namespace BITKit
AsGlobal = asGlobal;
}
}
#if Godot
#else
#if UNITY_64
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ExportAttribute : System.Attribute
{

View File

@ -97,6 +97,22 @@ namespace BITKit
throw new NullReferenceException($"没有找到{typeof(T).Name}");
}
}
public static bool TryGet<T>(out T value)
{
lock (dictionary)
{
if (dictionary.TryGetValue(typeof(T), out var obj))
{
if (obj is T i)
{
value = i;
return true;
}
}
}
value = default;
return false;
}
public static async Task<Interface> GetAsync<Interface>()
{
await TaskHelper.WaitUntil(() => dictionary.ContainsKey(typeof(Interface)) && dictionary[typeof(Interface) ]is not null);

View File

@ -0,0 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BITKit
{
}

View File

@ -19,5 +19,6 @@ namespace BITKit
}
return cacheName;
}
public static int Hash => typeof(T).GetHashCode();
}
}

View File

@ -107,6 +107,7 @@ namespace BITKit
if (index is not -1 && list.TryGetElementAt(index, out var nextElement))
{
nextElement.IsEntered = true;
OnEntry?.Invoke(nextElement);
nextElement.Entry();
try
{
@ -114,7 +115,7 @@ namespace BITKit
nextElement.Entered();
}
catch (OperationCanceledException){}
OnEntry?.Invoke(nextElement);
}
}
completed = true;

View File

@ -63,6 +63,11 @@ namespace BITKit
/// </summary>
ItemQuality Quality { get; }
bool CopyItemsFrom(IBasicItem item);
/// <summary>
/// 价值
/// </summary>
/// <returns></returns>
int Value=>10;
}
/// <summary>
/// 可配置化物品,通常用于配置
@ -84,6 +89,7 @@ namespace BITKit
public string AddressablePath { get; set; }
public string Description;
public ItemQuality Quality;
public int Value { get; set; }
/// <summary>
/// 本地属性
/// </summary>
@ -122,6 +128,7 @@ namespace BITKit
public bool CopyItemsFrom(IBasicItem item)
{
Value = item.Value;
Id=item.Id;
Name = item.Name;
AddressablePath = item.AddressablePath;

View File

@ -76,5 +76,20 @@ namespace BITKit
/// </summary>
event Action<IBasicItemContainer> OnRebuild;
/// <summary>
/// 是否已完成物品交换,例如false就是开始交换物品true就是已完成交换物品,可以处理物品了
/// </summary>
event Action<bool> OnRelease;
/// <summary>
/// 添加挂起句柄
/// </summary>
/// <param name="id"></param>
void AddHandle(int id);
/// <summary>
/// 移除挂起句柄
/// </summary>
/// <param name="id"></param>
void RemoveHandle(int id);
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BITKit
{
public interface IWorldItemObject
{
public IBasicItem Item { get; set; }
public event Action<IBasicItem> OnSetItem;
}
}

View File

@ -7,6 +7,21 @@ namespace BITKit
{
public static class MathE
{
public static int GetHash<T>(IEnumerable<T> self)
{
var hash = 0;
foreach (var x in self)
{
if (x is not null)
hash += x.GetHashCode();
}
return hash;
}
public static bool IndexInRange<T>(this IEnumerable<T> self, int index)
{
return index >= 0 && index < self.Count();
}
public static bool Equals<T>(params T[] objs)
{
var a = objs.FirstOrDefault();
@ -60,7 +75,8 @@ namespace BITKit
{
foreach (var x in b)
{
if (a.Contains(x))
var enumerable = a as T[] ?? a.ToArray();
if (enumerable.Contains(x))
{
}

View File

@ -11,6 +11,7 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using BITKit.IO;
using BITKit.UX;
using Cysharp.Threading.Tasks;
using Microsoft.CSharp;
using Newtonsoft.Json;
@ -129,6 +130,10 @@ namespace BITKit.Mod
{
public static async UniTask<ModPackage[]> SearchPackages()
{
DI.TryGet<IUXWaiting>(out var waiting);
var handle = waiting?.Get();
handle?.SetMessage("正在搜索Mod包");
var list=new List<ModPackage>();
var path = Path.Combine(Environment.CurrentDirectory, "Mods");
var dir = new DirectoryInfo(path);
@ -142,10 +147,13 @@ namespace BITKit.Mod
package.WorkDirectory = x.FullName;
list.Add(package);
}
waiting?.Release(handle);
return list.ToArray();
}
public static async UniTask Reload()
{
OnReload?.Invoke();
var mods = Mods;
foreach (var x in Mods)
{
@ -170,6 +178,7 @@ namespace BITKit.Mod
BIT4Log.LogException(e);
}
}
OnReloaded?.Invoke();
}
public static IMod[] Mods { get; private set; }=Array.Empty<IMod>();
@ -195,6 +204,9 @@ namespace BITKit.Mod
public static event Action<IMod> OnModInstalled;
public static event Action<IMod> OnModUnInstalled;
public static event Action OnReload;
public static event Action OnReloaded;
public static event Action<bool> OnLocked;
public static event Func<IMod,UniTask> OnModLoadAsync;
@ -266,37 +278,20 @@ namespace BITKit.Mod
while (_IsRunning)
{
_CacheMods.Clear();
while (_RegisterQueue.TryDequeue(out var mod))
{
_CacheMods.Add(mod);
mod.OnInitialize();
OnModLoad?.Invoke(mod);
BIT4Log.Log<ModService>($"加载Mod:{mod.GetType().FullName}");
}
foreach (var mod in _CacheMods)
{
await mod.OnInitializedAsync(_CancellationTokenSource.Token);
foreach (var x in OnModLoadAsync.CastAsFunc())
{
await x.Invoke(mod);
}
}
foreach (var mod in _CacheMods)
{
mod.OnInitialized();
OnModLoaded?.Invoke(mod);
}
DI.TryGet<IUXWaiting>(out var waiting);
_CacheMods.Clear();
while (_UnRegisterQueue.TryDequeue(out var mod))
{
var handle = waiting?.Get();
handle?.SetMessage($":正在卸载{mod.PackageName}");
mod.OnDispose();
_CacheMods.Add(mod);
OnModUnLoad?.Invoke(mod);
waiting?.Release(handle);
}
foreach (var mod in _CacheMods)
@ -314,6 +309,51 @@ namespace BITKit.Mod
OnModUnLoaded?.Invoke(mod);
BIT4Log.Log<ModService>($"卸载Mod:{mod.GetType().FullName}");
}
_CacheMods.Clear();
while (_RegisterQueue.TryDequeue(out var mod))
{
var handle = waiting?.Get();
handle?.SetMessage($"正在加载:{mod.PackageName}");
_CacheMods.Add(mod);
mod.OnInitialize();
OnModLoad?.Invoke(mod);
BIT4Log.Log<ModService>($"加载Mod:{mod.GetType().FullName}");
waiting?.Release(handle);
}
foreach (var mod in _CacheMods)
{
var handle = waiting?.Get();
handle?.SetMessage($"正在初始化:{mod.PackageName}");
await mod.OnInitializedAsync(_CancellationTokenSource.Token);
foreach (var x in OnModLoadAsync.CastAsFunc())
{
await x.Invoke(mod);
}
waiting?.Release(handle);
}
foreach (var mod in _CacheMods)
{
var handle = waiting?.Get();
handle?.SetMessage($":正在完成初始化中{mod.PackageName}");
mod.OnInitialized();
OnModLoaded?.Invoke(mod);
waiting?.Release(handle);
}
_CacheMods.Clear();
//Thread.Sleep(1000);
#if UNITY_64
await UniTask.Delay(1000);

View File

@ -1,3 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@ -36,8 +37,8 @@ namespace BITKit.Modification
/// </summary>
public interface IModifyElement
{
IModifyElement[] Require { get; }
IModifyElement[] Incompatible { get; }
IModifyElement[] Require => Array.Empty<IModifyElement>();
IModifyElement[] Incompatible => Array.Empty<IModifyElement>();
}
/// <summary>
/// 改装管理器
@ -65,17 +66,32 @@ namespace BITKit.Modification
}
public class ModifyManager : IModifyManager
{
public virtual IDictionary<IModifyElement, object> Modifies { get;private set; } =new Dictionary<IModifyElement,object>();
public virtual IDictionary<IModifyElement, object> Modified => new Dictionary<IModifyElement, object>(Modifies.Where(x=>x.Value is not null));
public virtual IDictionary<IModifyElement, object> Modifies { get; } =new Dictionary<IModifyElement,object>();
public virtual IDictionary<IModifyElement, object> Modified { get; } = new Dictionary<IModifyElement, object>();
public virtual void Add(IModifyElement modify, object obj)
{
if(Modified.ContainsKey(modify))
throw new NotSupportModifyException(new[]{modify});
var list = new List<IModifyElement>();
list.AddRange(Modified.Keys);
list.Add(modify);
if(list.All(x=>x.Require?.Any(y=>list.Contains(y)) is false))
throw new NotRequireModifyException(list.ToArray());
foreach (var x in list)
{
if(x.Require is null or {Length:0})continue;
foreach (var _x in x.Require)
{
if (list.Contains(_x) is false)
throw new NotRequireModifyException(x.Require);
}
}
//你知道怎么做,帮我做一下
var incompatible = list.Where(x=>x.Incompatible is not null).SelectMany(x => x.Incompatible).ToArray();
if(MathE.Contains(incompatible,list))

View File

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

View File

@ -0,0 +1,50 @@
using System.Collections;
using System.Collections.Generic;
namespace BITKit.Probability
{
/// <summary>
/// 概率属性
/// </summary>
public interface IProbabilityProperty
{
}
/// <summary>
/// 概率服务
/// </summary>
public interface IProbabilityService
{
/// <summary>
/// 根据概率属性获取值
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
object GetValueObject(params IProbabilityProperty[] properties);
/// <summary>
/// 根据概率属性获取值
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
object[] GetValuesObject(params IProbabilityProperty[] properties);
}
/// <summary>
/// 泛型概率服务
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IProbabilityService<T>:IProbabilityService
{
/// <summary>
/// 根据概率属性获取值
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
T GetValue(params IProbabilityProperty[] properties);
/// <summary>
/// 根据概率属性获取值
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
T[] GetValues(params IProbabilityProperty[] properties);
}
}

View File

@ -70,82 +70,84 @@ namespace BITKit
{
foreach (var x in factory)
{
AddPropertyInternal(x);
properties.Add(x);
}
}
Dictionary<string, object> properties=new();
private readonly List<object> properties=new();
public T GetProperty<T>()
{
return (T)properties[typeof(T).FullName];
return properties.OfType<T>().First();
}
public void SetProperty<T>(T value)
{
properties.Insert(typeof(T).FullName, value);
for (var i = 0; i < properties.Count; i++)
{
if (properties[i] is T)
properties[i] = value;
}
}
public bool Contains<T>()
{
return properties.ContainsKey(typeof(T).FullName);
return properties.OfType<T>().FirstOrDefault() is not null;
}
public T GetOrCreateProperty<T>()
{
return GetOrAddProperty(Activator.CreateInstance<T>);
}
public T GetOrAddProperty<T>(Func<T> addFactory)
{
if (properties.TryGetValue(typeof(T).FullName, out var x))
foreach (var obj in properties)
{
return (T)x;
}
else
{
AddPropertyInternal(addFactory.Invoke());
//properties.Add(typeof(T).FullName, x = addFactory.Invoke());
return (T)x;
if (obj is T t) return t;
}
T x;
properties.Add(x = addFactory.Invoke());
return x;
}
public bool TryGetProperty<T>(out T value)
{
if (properties.TryGetValue(typeof(T).FullName, out var x))
try
{
value = (T)x;
value = properties.OfType<T>().First();
return true;
}
else
catch (InvalidOperationException)
{
value=default(T);
value = default;
return false;
}
}
public bool TryRemoveProperty<T>()
{
if(properties.TryGetValue(typeof(T).FullName, out var x))
var removed=false;
foreach (var value in properties.OfType<T>().ToArray())
{
properties.Remove(typeof(T).Name);
return true;
properties.Remove(value);
removed = true;
}
return false;
// if(properties.TryGetValue(typeof(T).FullName, out var x))
// {
// properties.Remove(typeof(T).Name);
// return true;
// }
return removed;
}
public bool TrySetProperty<T>(T value)
{
bool result = false;
foreach (var pair in properties.Where(x=>x.Value is T).ToArray())
var current = properties.OfType<T>().FirstOrDefault();
if (current is not null)
{
properties[pair.Key] = value;
result = true;
properties.Remove(current);
return true;
}
return result;
// if (properties.TryGetValue(typeof(T).FullName, out var x))
// {
// properties[typeof(T).FullName] = value;
// return true;
// }
// else
// {
// return false;
// }
properties.Add(value);
return false;
}
public object[] GetProperties()=>properties.Values.Distinct().ToArray();
public object[] GetProperties() => properties.ToArray();
public void Read(BinaryReader r)
{
@ -169,23 +171,8 @@ namespace BITKit
public bool CopyPropertiesFrom(IPropertable propertable)
{
ClearProperties();
foreach (var x in propertable.GetProperties())
{
AddPropertyInternal(x);
}
properties.AddRange( propertable.GetProperties());;
return true;
}
private void AddPropertyInternal(object value)
{
if (value is ICloneable cloneable)
{
value = cloneable.Clone();
}
properties.Set(value.GetType().FullName, value);
foreach (var att in value.GetType().GetCustomAttributes<CustomTypeAttribute>())
{
properties.Set(att.Type!.FullName, value);
}
}
}
}

View File

@ -13,6 +13,7 @@ using BITKit.Mod;
#if UNITY_64
using System.Collections;
using BITKit.UX;
using UnityEngine;
using UnityEngine.Rendering;
using static BITKit.Mod.DotNetSdkRoslynService;
@ -60,6 +61,11 @@ namespace BITKit
public static Assembly Compile(params string[] codes)
{
var outputPath = PathHelper.GetTempFilePath();
DI.TryGet<IUXWaiting>(out var waiting);
var handle = waiting?.Get();
handle?.SetMessage("正在编译");
try
{
if (Installed is false)
@ -126,6 +132,8 @@ namespace BITKit
StandardInputEncoding = System.Text.Encoding.GetEncoding("gb2312"),
StandardOutputEncoding = System.Text.Encoding.GetEncoding("gb2312"),
};
var process = new Process()
{
StartInfo = StartInfo,
@ -165,12 +173,14 @@ namespace BITKit
}
}
waiting?.Release(handle);
return Assembly.Load(bytes);
}
catch (Exception e)
{
waiting?.Release(handle);
throw new Exception($"编译失败:{e}");
}

View File

@ -2,6 +2,7 @@ namespace BITKit
{
public interface ITag
{
int Hash { get; }
string[] GetTags();
}
}

View File

@ -0,0 +1,13 @@
using System.Collections;
using System.Collections.Generic;
namespace BITKit.UX
{
public interface IUXBarService
{
void SetOrCreate(int id,string name,float value,object data = null);
void Remove(int id);
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BITKit.UX
{
public interface IUXWaitingHandle
{
string Message { get; set; }
object Container { get; }
public void SetMessage(string message)=>Message=message;
}
public interface IUXWaiting
{
IUXWaitingHandle Get();
void Release(IUXWaitingHandle handle);
}
}

View File

@ -145,6 +145,13 @@ namespace BITKit
{
value = _directDirection.Get(key);
}
public T GetOrAddDirect<T>(int key,Func<T> factory)
{
if (_directDirection.TryGetValue(key, out var v)) return v is T t ? t : default;
var value = factory.Invoke();
_directDirection.Add(key, value);
return value;
}
public void GetDirect<T>(int key,out T value)
{
try

View File

@ -12,4 +12,5 @@
T Get();
void Set(T t);
}
}

View File

@ -17,7 +17,8 @@ namespace BITKit
{
public static string[] Cast(this IEnumerable<IReference> self)
{
return self.Select(x => x.Get()).ToArray();
return self.Select(Get).ToArray();
string Get(IReference x) => x.Value;
}
}

View File

@ -47,7 +47,7 @@
"id": "07dfe885-d709-4482-9686-57f671b1aed6",
"expectedControlType": "Button",
"processors": "",
"interactions": "",
"interactions": "Press",
"initialStateCheck": false
},
{

View File

@ -22,6 +22,8 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: BITSky
m_Shader: {fileID: 103, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords:
- _SUNDISK_HIGH_QUALITY
@ -31,6 +33,7 @@ Material:
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:

View File

@ -26,6 +26,7 @@ RenderTexture:
m_UseDynamicScale: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1

View File

@ -1,10 +1,10 @@
{
"name": "BITKit.WorkdChunk",
"name": "BITKit.Pool",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:a9eec99827e569e45bfe3e5ea7494591",
"GUID:d8b63aba1907145bea998dd612889d6b"
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -0,0 +1,78 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using AYellowpaper.SerializedCollections;
using UnityEngine;
namespace BITKit
{
public class PoolService : MonoBehaviour
{
private static readonly ConcurrentDictionary<string, Transform> _Prefabs = new();
private static readonly ConcurrentDictionary<string,UnityPool<Transform>> _Pools = new();
public static Transform Get(Transform prefab)
{
_Prefabs.TryAdd(prefab.name, prefab);
var pool = _Pools.GetOrAdd(prefab.name, CreatePool);
return pool.Get();
}
public static bool TryGet(Transform prefab, out Transform instance)
{
_Prefabs.TryAdd(prefab.name, prefab);
var pool = _Pools.GetOrAdd(prefab.name, CreatePool);
instance = null;
if (pool.InstanceCount>=pool.DefaultCapacity) return false;
instance = pool.Get();
return true;
}
public static void Release(string name,Transform instance)
{
var pool = _Pools.GetOrAdd(name, CreatePool);
pool.Return(instance);
}
private static UnityPool<Transform> CreatePool(string name)
{
var pool = new UnityPool<Transform>
{
Prefab = _Prefabs[name],
OnReturn = null,
};
return pool;
}
[SerializeField] private SerializedDictionary<Transform,int> initialCapacity;
private void Start()
{
foreach (var (key, value) in initialCapacity)
{
var pool = new UnityPool<Transform>
{
Prefab = key,
DefaultCapacity = value,
Root = transform,
OnReturn = null,
};
_Prefabs.TryAdd(key.name, key);
_Pools.TryAdd(key.name, pool);
var list = new List<Transform>();
for (var i = 0; i < pool.DefaultCapacity; i++)
{
list.Add(pool.Get());
}
foreach (var x in list)
{
x.gameObject.SetActive(false);
pool.Return(x);
}
}
}
private void OnDestroy()
{
_Prefabs.Clear();
_Pools.Clear();
}
}
}

View File

@ -1,117 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using AYellowpaper.SerializedCollections;
using UnityEngine;
using YooAsset;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace BITKit
{
public class AddressableHelper:MonoBehaviour
{
private static IDictionary<ulong,string> PathsById { get; } = new Dictionary<ulong, string>();
public static T Get<T>(ulong id) where T : Object
{
var task = YooAssets.LoadAssetAsync<T>(PathsById[id]);
task.WaitForAsyncComplete();
return task.AssetObject.As<T>();
}
[SerializeField] private SerializedDictionary<ulong,string> pathsById;
private void Start()
{
PathsById.Clear();
foreach (var x in pathsById)
{
PathsById.Add(x.Key,x.Value);
}
}
#if UNITY_EDITOR
[BIT]
private void BuildCache()
{
var guids = AssetDatabase.FindAssets($"t:Object",new[] {"Assets"});
var paths = guids.Select(AssetDatabase.GUIDToAssetPath);
var objects = new List<IAddressable>();
var stringBuilder = new System.Text.StringBuilder();
foreach (var path in paths)
{
var asset = AssetDatabase.LoadAssetAtPath<Object>(path);
switch (asset)
{
case GameObject go when go.TryGetComponent(out IAddressable addressable):
objects.Add(addressable);
break;
case IAddressable addressable:
objects.Add(addressable);
break;
}
}
stringBuilder.AppendLine($"所有资源数量:{guids.Length},其中包含{objects.Count}个Addressable资源");
pathsById.Clear();
foreach (var x in objects)
{
if (x is not Object unityObject) continue;
//if (x.AddressableId is ulong.MinValue or ulong.MaxValue)
{
x.AddressableId = RandomUlong.NextUlong;
EditorUtility.SetDirty(unityObject);
}
try
{
if (x.AddressableId is not ulong.MinValue && !string.IsNullOrEmpty(x.AddressablePath))
{
if (pathsById.TryAdd(x.AddressableId, x.AddressablePath))
{
}
else
{
stringBuilder.AppendLine($"资源{unityObject.name}的AddressableId:{x.AddressableId}已经存在");
}
}
else
{
stringBuilder.AppendLine($"{unityObject.name}的AddressableId或AddressablePath为空");
}
}
catch (Exception e)
{
stringBuilder.AppendLine($"{unityObject.name}遇到了错误:{e.Message}");
}
}
EditorUtility.SetDirty(this);
Debug.Log(stringBuilder);
}
#endif
}
public static class RandomUlong
{
private static readonly System.Random _random = new();
public static ulong NextUlong
{
get
{
var buf = new byte[8];
_random.NextBytes(buf);
return BitConverter.ToUInt64(buf, 0);
}
}
}
}

View File

@ -10,13 +10,17 @@ namespace BITKit
private void OnAnimatorMove()
{
if (root)
root.SendMessage(nameof(OnAnimatorMove));
root.SendMessageUpwards(nameof(OnAnimatorMove),SendMessageOptions.DontRequireReceiver);
}
private void AIAnimationEvent(string actionName)
{
if (root)
root.SendMessage(nameof(AIAnimationEvent), actionName);
root.SendMessage(nameof(AIAnimationEvent), actionName,SendMessageOptions.DontRequireReceiver);
}
public void AnimationEvent(string eventName)
{
if(root)
root.SendMessage(nameof(AnimationEvent), eventName,SendMessageOptions.DontRequireReceiver);
}
}
}

View File

@ -0,0 +1,90 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using AYellowpaper.SerializedCollections;
using BITKit.IO;
using Cysharp.Threading.Tasks;
using UnityEngine;
using YooAsset;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace BITKit
{
public class AddressableHelper:MonoBehaviour
{
private static IDictionary<ulong,string> PathsById { get; } = new Dictionary<ulong, string>();
public static T Get<T>(ulong id) where T : Object
{
var task = YooAssets.LoadAssetAsync<T>(PathsById[id]);
task.WaitForAsyncComplete();
return task.AssetObject.As<T>();
}
private void Start()
{
PathsById.Clear();
YooAssetUtils.OnPackageRegistered += OnRegister;
foreach (var registeredResource in YooAssetUtils.RegisteredResourcePackages)
{
OnRegister(registeredResource.PackageName);
}
destroyCancellationToken.Register(() =>
{
YooAssetUtils.OnPackageRegistered -= OnRegister;
});
}
private static async void OnRegister(string obj)
{
Stopwatch stopWatcher = new();
stopWatcher.Start();
BIT4Log.Log<AddressableHelper>($"开始注册:{obj}的资源");
var package = YooAssets.GetPackage(obj);
var assetInfos = package.GetAssetInfos(nameof(IAddressable));
var reportBuilder = new StringBuilder();
foreach (var x in assetInfos)
{
var asyncHandle = YooAssets.LoadAssetAsync(x.AssetPath);
await asyncHandle;
if (asyncHandle.AssetObject is IAddressable addressable)
{
if (PathsById.TryAdd(addressable.AddressableId, addressable.AddressablePath) is false)
{
var existing = PathsById[addressable.AddressableId];
BIT4Log.Warning<AddressableHelper>($"{addressable.AddressablePath}的Id:{addressable.AddressableId}已被注册为{existing}");
}
reportBuilder.AppendLine($"{addressable.AddressablePath}的Id:{addressable.AddressableId}");
}
}
BIT4Log.Log<AddressableHelper>($"注册:{obj}的资源完成,耗时:{stopWatcher.ElapsedMilliseconds}ms");
BIT4Log.Log<AddressableHelper>(reportBuilder.ToString());
}
}
public static class RandomUlong
{
private static readonly System.Random _random = new();
public static ulong NextUlong
{
get
{
var buf = new byte[8];
_random.NextBytes(buf);
return BitConverter.ToUInt64(buf, 0);
}
}
}
}

View File

@ -9,6 +9,6 @@ namespace BITKit
public float Sensitivity = 1.81f;
public float TouchSensitivity = 0.22f;
public float M_Yaw = 0.022f;
public float Fov = 75;
public float Fov = 90;
}
}

View File

@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.IData
{
public interface IBindableData
{
object Data { get; }
}
public interface IBindableData<T>:IBindableData
{
new T Data { get; }
}
public readonly struct BindableData:IBindableData
{
public BindableData(object data)
{
Data = data;
}
public object Data { get; }
}
public readonly struct BindableData<T>:IBindableData<T>
{
public T Data { get; }
object IBindableData.Data => Data;
public BindableData(T data)
{
Data = data;
}
}
}

View File

@ -1,28 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BITKit.Sensors;
namespace BITKit.Entities
{
public class EntityAudioObject : EntityBehavior, IAudioObject
{
float volume;
public override void OnStart()
{
UnityEntity.AddListener<AudioSO>(OnAuioSO);
}
public override void OnFixedUpdate(float deltaTime)
{
volume = Mathf.Lerp(volume, 0, deltaTime);
}
public float GetVolume()
{
return volume;
}
void OnAuioSO(AudioSO so)
{
if (so.distance > volume)
volume = so.distance;
}
}
}

View File

@ -10,14 +10,12 @@ namespace BITKit.Entities
{
public interface IDamageType { }
public interface IDamageService
{
public event Action<DamageMessage> OnEntityDamaged;
public event Action<DamageMessage> OnEntityKilled;
void Execute(DamageMessage damageMessage);
}
public struct MeleeDamageMessage : IDamageType
{
public bool Bleeding;
@ -112,6 +110,7 @@ namespace BITKit.Entities
}
public interface IDamagable
{
string Tag { get; }
IHealth Health { get; }
IUnityEntity UnityEntity { get; }
Rigidbody Rigidbody { get; }
@ -119,7 +118,7 @@ namespace BITKit.Entities
}
public class DamageService:MonoBehaviour,IDamageService
{
internal static IDamageService Singleton { get; set; }
public static IDamageService Singleton { get;private set; }
private readonly Queue<DamageMessage> Messages = new();
[SerializeReference,SubclassSelector] private INetClient netClient;

View File

@ -105,7 +105,7 @@ namespace BITKit.Entities
else
{
var damage = damageMessage.Damage;
foreach (var x in OnDamageFactory.CastAsFunc().Reverse())
foreach (var x in OnDamageFactory.CastAsFunc())
{
damage = x.Invoke(damageMessage, damage);
if (damage <= 0)

View File

@ -4,19 +4,55 @@ using UnityEngine;
using BITKit;
namespace BITKit.Entities
{
public class EntityHitbox : EntityBehavior,IDamagable
public class EntityHitbox : MonoBehaviour,IDamagable
{
public IHealth Health => _health;
public string Tag => tag is null ? base.tag : tag.Value;
public IHealth Health
{
get
{
EnsureConfigure();
return _health;
}
}
public IUnityEntity UnityEntity
{
get
{
EnsureConfigure();
return _unityEntity;
}
}
public Rigidbody Rigidbody
{
get=>m_rigidbody;
set=>m_rigidbody=value;
}
IUnityEntity IDamagable.UnityEntity => UnityEntity;
public Rigidbody Rigidbody => m_rigidbody;
public void GiveDamage(DamageMessage message)
{
UnityEntity.Invoke(message);
if (_unityEntity is not null)
UnityEntity.Invoke(message);
}
[SerializeField]private Rigidbody m_rigidbody;
[Inject]
[SerializeField]private Rigidbody m_rigidbody;
[SerializeReference, SubclassSelector] private new IReference tag;
[Inject(true)]
private IHealth _health;
private IUnityEntity _unityEntity;
private bool _initialized;
private void EnsureConfigure()
{
if (_initialized) return;
_unityEntity = GetComponentInParent<IUnityEntity>(true);
_unityEntity?.Inject(this);
_initialized = true;
}
}
}

View File

@ -0,0 +1,77 @@
using System.Collections;
using System.Collections.Generic;
using BITFALL.Rig;
using BITKit.Entities;
using Cysharp.Threading.Tasks.Triggers;
using UnityEditor;
using UnityEngine;
namespace BITKit
{
public class EntityHitboxBuilder : MonoBehaviour
{
[SerializeField] private Collider[] colliders;
[SerializeField] private EntityHitbox[] hitboxes;
[SerializeReference,SubclassSelector] private IReference[] tagReferences;
[BIT]
private void Build()
{
foreach (var x in hitboxes)
{
DestroyImmediate(x.gameObject);
}
hitboxes = new EntityHitbox[colliders.Length];
for (var i = 0; i < colliders.Length; i++)
{
var collider = colliders[i];
var newGameObject = new GameObject($"Hitbox_{collider.gameObject.name}");
newGameObject.layer = gameObject.layer;
newGameObject.transform.SetParent(transform);
var transform1 = collider.transform;
newGameObject.transform.position = transform1.position;
newGameObject.transform.rotation = transform1.rotation;
var hitbox =hitboxes[i] = newGameObject.AddComponent<EntityHitbox>();
var tag = newGameObject.AddComponent<Tag>();
var tickOverride = newGameObject.AddComponent<TickOverrideTransform>();
tickOverride.Source = newGameObject.transform;
tickOverride.Target = collider.transform;
if (collider.TryGetComponent<Rigidbody>(out var newRigidbody))
{
hitbox.Rigidbody = newRigidbody;
}
switch (collider)
{
case BoxCollider boxCollider:
var box = newGameObject.AddComponent<BoxCollider>();
box.size = boxCollider.size;
box.center = boxCollider.center;
break;
case SphereCollider sphereCollider:
var sphere = newGameObject.AddComponent<SphereCollider>();
sphere.radius = sphereCollider.radius;
sphere.center = sphereCollider.center;
break;
case CapsuleCollider capsuleCollider:
var capsule = newGameObject.AddComponent<CapsuleCollider>();
capsule.radius = capsuleCollider.radius;
capsule.height = capsuleCollider.height;
capsule.direction = capsuleCollider.direction;
capsule.center = capsuleCollider.center;
break;
}
tag.SetTags(tagReferences);
EditorUtility.SetDirty(hitbox);
EditorUtility.SetDirty(tag);
}
EditorUtility.SetDirty(this);
}
}
}

View File

@ -1,144 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BITKit.Entities.Physics;
using UnityEngine;
namespace BITKit.Entities
{
[CustomType(typeof(IEntityPhysics))]
public class EntityPhysics : EntityBehavior,IEntityPhysics
{
[SerializeField] private Animator animator;
[SerializeField] private Rigidbody[] rigidbodies;
[SerializeField] private Collider[] ragdollColliders;
[SerializeField] private Joint[] joints;
[SerializeField] private new Rigidbody rigidbody;
[Inject(true)]
private IHealth _health;
[Inject(true)] private IEntityOverride _override;
private readonly Dictionary<Joint,ConfigurableJointMotion> _jointXMotions=new();
private readonly Dictionary<Joint,ConfigurableJointMotion> _jointYMotions=new();
private readonly Dictionary<Joint,ConfigurableJointMotion> _jointZMotions=new();
private readonly Dictionary<Joint,ConfigurableJointMotion> _jointAngularXMotions=new();
private readonly Dictionary<Joint,ConfigurableJointMotion> _jointAngularYMotions=new();
private readonly Dictionary<Joint,ConfigurableJointMotion> _jointAngularZMotions=new();
public override void OnAwake()
{
_health.OnSetAlive += OnSetAlive;
foreach (var x in joints)
{
switch (x)
{
case ConfigurableJoint configurableJoint:
_jointXMotions.Add(configurableJoint, configurableJoint.xMotion);
_jointYMotions.Add(configurableJoint, configurableJoint.yMotion);
_jointZMotions.Add(configurableJoint, configurableJoint.zMotion);
_jointAngularXMotions.Add(configurableJoint, configurableJoint.angularXMotion);
_jointAngularYMotions.Add(configurableJoint, configurableJoint.angularYMotion);
_jointAngularZMotions.Add(configurableJoint, configurableJoint.angularZMotion);
break;
}
}
if (_override is not null)
_override.OnOverride += OnOverride;
}
private void OnOverride(bool obj)
{
EnsureConf();
}
private void OnSetAlive(bool alive)
{
EnsureConf();
}
private async void EnsureConf()
{
var allow = (_health, _override) switch
{
(not null,not null)=>_health.IsAlive || _override.IsOvering,
(not null,null)=>_health.IsAlive,
(null,not null)=>_override.IsOvering,
_=>false,
};
if (animator)
{
animator.enabled = allow;
}
try
{
await Task.Delay(10, destroyCancellationToken);
if (destroyCancellationToken.IsCancellationRequested) return;
foreach (var x in rigidbodies)
{
//x.isKinematic = alive;
x.gameObject.SetActive(!allow);
}
foreach (var x in ragdollColliders)
{
x.enabled = !allow;
}
OnSetPhysics?.Invoke(!allow);
}
catch (OperationCanceledException)
{
}
foreach (var joint in joints)
{
switch (joint)
{
case ConfigurableJoint configurableJoint:
configurableJoint.xMotion = allow ? _jointXMotions[joint] : ConfigurableJointMotion.Free;
configurableJoint.yMotion = allow ? _jointYMotions[joint] : ConfigurableJointMotion.Free;
configurableJoint.zMotion = allow ? _jointZMotions[joint] : ConfigurableJointMotion.Free;
configurableJoint.angularXMotion =
allow ? _jointAngularXMotions[joint] : ConfigurableJointMotion.Free;
configurableJoint.angularYMotion =
allow ? _jointAngularYMotions[joint] : ConfigurableJointMotion.Free;
configurableJoint.angularZMotion =
allow ? _jointAngularZMotions[joint] : ConfigurableJointMotion.Free;
break;
}
}
}
public Vector3 Center => rigidbody.worldCenterOfMass;
public bool IsPhysics { get; private set; }
public Vector3 Velocity
{
get=>rigidbody.velocity;
set
{
foreach (var x in rigidbodies)
{
x.velocity = value;
}
}
}
public Action<bool> OnSetPhysics { get; set; }
public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force)
{
foreach (var x in rigidbodies)
{
x.AddForce(force, mode);
}
}
public void AddTorque(Vector3 torque, ForceMode mode = ForceMode.Force)
{
foreach (var x in rigidbodies)
{
x.AddTorque(torque, mode);
}
}
}
}

View File

@ -36,7 +36,7 @@ namespace BITKit.Entities
foreach (var fieldInfo in obj
.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(fieldInfo=>fieldInfo.GetCustomAttribute<InjectAttribute>() is not null))
.Where(fieldInfo=>fieldInfo.GetCustomAttribute<InjectAttribute>(true) is not null))
{
var type = fieldInfo.FieldType;
var attribute = fieldInfo.GetCustomAttribute<InjectAttribute>();

View File

@ -7,7 +7,8 @@
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:9354affc93e0f3e4a904785e7d4c0f59",
"GUID:c56f2ae4d67b9b947a600c84225206a2"
"GUID:c56f2ae4d67b9b947a600c84225206a2",
"GUID:296866320aab85a42a0403bf684bac59"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -1,12 +1,11 @@
{
"name": "BITKit.Entities.Physics.Runtime",
"name": "BITKit.Entities.World.Runtime",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:709caf8d7fb6ef24bbba0ab9962a3ad0",
"GUID:a3de65b07192e7d49bad7b4032d681de",
"GUID:7efac18f239530141802fb139776f333"
"GUID:1193c2664d97cc049a6e4c486c6bce71",
"GUID:bdb069e155d2f944cb1bf28602b6d4c1"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -0,0 +1,24 @@
using System.Collections;
using System.Collections.Generic;
using BITKit.Entities;
using Quadtree;
using UnityEngine;
namespace BITKit.OpenWorld
{
public class EntityLOD : EntityBehavior,IWorldChunkObject
{
public Bounds GetBounds()
{
throw new System.NotImplementedException();
}
public Node<IWorldChunkObject> ParentNode { get; set; }
public void QuadTree_Root_Initialized(IQuadtreeRoot<IWorldChunkObject, Node<IWorldChunkObject>> root)
{
}
public int Id { get; set; }
public int Lod { get; set; }
}
}

View File

@ -1,5 +1,5 @@
{
"name": "I18N",
"name": "Plugins.I18N",
"rootNamespace": "",
"references": [],
"includePlatforms": [],

View File

@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;
@ -17,6 +18,10 @@ namespace BITKit
[SerializeField] private Vector3 positionWeight=Vector3.one;
[SerializeField] private bool debug;
[SerializeField,ReadOnly(HideLabel = true)] private string debugInfo;
private Vector3 currentPosition;
private Quaternion currentRotation=Quaternion.identity;
@ -25,6 +30,8 @@ namespace BITKit
private Transform _transform;
private readonly StringBuilder reportBuilder = new();
public Vector3 Position
{
get => _transform.localPosition;
@ -36,9 +43,13 @@ namespace BITKit
set => _transform.localRotation = value;
}
public void AddPosition(Vector3 value)
public void AddPosition(Vector3 value,string info=null)
{
currentPosition += value;
if (debug)
{
reportBuilder.AppendLine($"{info}:{value}");
}
}
// public void AddEuler(Vector3 value)
@ -46,11 +57,16 @@ namespace BITKit
// //currentEuler += value;
// currentRotation *= Quaternion.Euler(value);
// }
public void AddRotation(Quaternion value)
public void AddRotation(Quaternion value, string info = null)
{
currentRotation *= value;
if (debug)
{
reportBuilder.AppendLine($"{info}:{value}");
}
}
public void SetGlobalPosition(Vector3 value)
{
globalPosition = value;
@ -103,6 +119,12 @@ namespace BITKit
currentRotation = Quaternion.identity;
currentPosition = Vector3.zero;
if (debug)
{
debugInfo = reportBuilder.ToString();
reportBuilder.Clear();
}
}
}
}

View File

@ -16,11 +16,14 @@ namespace BITKit
[SerializeField] private Vector3 positionWeight = Vector3.one;
[SerializeReference,SubclassSelector] private IReference autoReference;
[SerializeReference,SubclassSelector] private IReference nameReference;
//[SerializeField] private Vector3 rotationAdditive = Vector3.one;
private Transform Transform;
private string nameCache;
private void Start()
{
nameCache = nameReference is not null?nameReference.Value:gameObject.name;
Transform = transform;
var components =
GetComponentsInParent<LocationAdditive>(true)
@ -39,7 +42,6 @@ namespace BITKit
reportBuilder.AppendLine(x.autoReference is not null ? $"{x.autoReference.Value}@{x.transform.name}" : x.name);
}
BIT4Log.Log<LocationAdditiveElement>($"找不到对应的LocationAdditive:{autoReference.Value} \n{reportBuilder}");
throw;
}
}
@ -58,11 +60,12 @@ namespace BITKit
private void Tick()
{
if (!locationAdditive) return;
var localPosition = Transform.localPosition;
locationAdditive.AddPosition(Vector3.Scale(localPosition, positionWeight));
locationAdditive.AddPosition(Vector3.Scale(localPosition, positionWeight), nameReference?.Value);
//locationAdditive.AddRotation(Transform.localRotation * Quaternion.Euler(rotationAdditive));
locationAdditive.AddRotation(Transform.localRotation);
locationAdditive.AddRotation(Transform.localRotation, nameCache);
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BITKit.UX;
#if UNITY_EDITOR
using UnityEditor;
@ -18,6 +19,9 @@ namespace BITKit.Mod
[SerializeField] private bool loadLocalPackageOnStart;
private async void Start()
{
DI.TryGet<IUXWaiting>(out var waiting);
var handle = waiting?.Get();
handle?.SetMessage("正在初始化Mod服务");
if (Application.isEditor is false)
{
@ -71,6 +75,8 @@ namespace BITKit.Mod
{
ModService.OnPackageLoad-=OnPackageLoad;
});
waiting?.Release(handle);
}
private void OnPackageLoad(ModPackage obj)
@ -106,8 +112,9 @@ namespace BITKit.Mod
{
dll = Path.Combine(folder,"Data", "MonoBleedingEdge", "lib","mono","unityjit-win32","Facades",dllName);
}
#else
var dll = System.IO.Path.Combine(Environment.CurrentDirectory,$"{Application.productName}_Data", "Managed", dllName);
dll = System.IO.Path.Combine(Environment.CurrentDirectory,$"{Application.productName}_Data", "Managed", dllName);
#endif
return File.Exists(dll);
}

View File

@ -0,0 +1,20 @@
{
"name": "BITKit.MotionMatching",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:e34a5702dd353724aa315fb8011f08c3",
"GUID:296866320aab85a42a0403bf684bac59"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,50 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.Animations
{
public interface IMotionMatchingObject
{
}
public interface IMotionMatchingService : IObjectMatcher<string,IMotionMatchingObject>
{
}
public interface IMotionMatchingClip
{
public AnimationClip Clip { get; }
}
public interface IMotionMatchingDualClip
{
public AnimationClip Clip1 { get; }
public AnimationClip Clip2 { get; }
}
public interface IMotionMatchingSequence
{
public AnimationClip[] Sequence { get; }
}
[Serializable]
public struct MotionMatchingClip:IMotionMatchingObject,IMotionMatchingClip
{
[SerializeField] private AnimationClip clip;
public AnimationClip Clip => clip;
}
[Serializable]
public struct MotionMatchingSequence:IMotionMatchingObject,IMotionMatchingSequence
{
[SerializeField] private AnimationClip[] sequence;
public AnimationClip[] Sequence => sequence;
}
[Serializable]
public struct MotionMatchingDualClip:IMotionMatchingObject,IMotionMatchingDualClip
{
[SerializeField] private AnimationClip clip1;
[SerializeField] private AnimationClip clip2;
public AnimationClip Clip1 => clip1;
public AnimationClip Clip2 => clip2;
}
}

View File

@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace BITKit.Animations
{
public class ScriptableMotionMatchingObject : ScriptableObject,IObjectElement<string,IMotionMatchingObject>
{
[SerializeReference,SubclassSelector] private IReference[] tags;
[SerializeReference,SubclassSelector] private IMotionMatchingObject value;
public bool IsMatch(string[] searchKey)=>MathE.Contains(tags.Cast(),searchKey);
public IMotionMatchingObject GetValue() => value;
}
}

View File

@ -0,0 +1,77 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using BITKit.Mod;
using UnityEngine;
using YooAsset;
namespace BITKit.Animations
{
[Serializable]
public struct ScriptableMotionMatchingSingleton:IMotionMatchingService
{
public bool TryMatch(out IMotionMatchingObject value, string[] key)=>ScriptableMotionMatchingService.Singleton.TryMatch(out value,key);
}
public class ScriptableMotionMatchingService : MonoBehaviour,IMotionMatchingService
{
internal static ScriptableMotionMatchingService Singleton { get; private set; }
internal static readonly ConcurrentDictionary<int, IMotionMatchingObject> Cache = new();
private ObjectMatcher<string,IMotionMatchingObject> _objects;
private void Awake()
{
Singleton = this;
}
private void OnEnable()
{
ModService.OnReloaded += Rebuild;
}
private void Start()
{
Rebuild();
}
private void OnDisable()
{
ModService.OnReloaded -= Rebuild;
}
public bool TryMatch(out IMotionMatchingObject value, string[] key)
{
value = Cache.GetOrAdd(MathE.GetHash(key),Add);
return value is not null;
IMotionMatchingObject Add(int hash)
{
if (_objects.TryMatch(out IMotionMatchingObject x, key) is false)
{
BIT4Log.Log<ScriptableMotionMatchingService>($"找不到对应的MotionMatchingObject:{string.Join(",", key)}");
return null;
}
return x;
}
}
private void Rebuild()
{
Cache.Clear();
var list = new List<ScriptableMotionMatchingObject>();
var tags = new []{nameof(IMotionMatchingObject)};
foreach (var x in YooAssets.GetAssetInfos(tags))
{
var assetHandle =YooAssets.LoadAssetSync<ScriptableMotionMatchingObject>(x.AssetPath);
assetHandle.WaitForAsyncComplete();
list.Add(assetHandle.AssetObject.As<ScriptableMotionMatchingObject>());
}
_objects = new ObjectMatcher<string, IMotionMatchingObject>
{
list = list.ToArray(),
};
}
}
}

View File

@ -16,7 +16,9 @@
"nunit.framework.dll"
],
"autoReferenced": true,
"defineConstraints": [],
"defineConstraints": [
"UNITY_EDITOR"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -1,11 +1,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using UnityEngine;
namespace BITKit
{
public interface IClosePoint
{
Collider Collider { get; }
bool TryGetClosePoint(out Vector3 vector3);
}
[System.Serializable]
@ -14,12 +17,15 @@ namespace BITKit
public Transform root;
public LayerMask layerMask;
public float distance;
public Collider Collider { get; set; }
public bool TryGetClosePoint(out Vector3 vector3)
{
vector3 = default;
if (UnityEngine.Physics.Raycast(root.position, root.forward, out var raycastHit, distance, layerMask))
{
var collider = raycastHit.collider;
if (collider.isTrigger) return false;
switch (collider)
{
case MeshCollider meshCollider:
@ -42,6 +48,8 @@ namespace BITKit
if(hit!= collider)
return false;
}
Collider = collider;
return true;
//return vector3.y >= collider.bounds.center.y + collider.bounds.extents.y;
//return true;
@ -66,13 +74,30 @@ namespace BITKit
public Vector3 StartPosition;
public Vector3 EndPosition;
public Collider Collider { get; set; }
private Rigidbody rigidbody;
private bool isInitialized;
public bool TryGetClosePoint(out Vector3 vector3)
{
if (isInitialized is false)
{
rigidbody = groundReference.GetComponent<Rigidbody>();
isInitialized = true;
}
if (rigidbody)
{
vector3 = default;
if (rigidbody.velocity.GetLength() < 0.1f) return false;
}
var reportBuilder = new System.Text.StringBuilder();
var forward = root.forward;
var startPosition = groundReference.position;
startPosition.y = root.position.y;
var sourceStartPosition = groundReference.position;
sourceStartPosition.y = root.position.y;
var startPosition = sourceStartPosition;
var collider = UnityEngine.Physics.OverlapSphere(startPosition, radius, layerMask);
@ -80,17 +105,23 @@ namespace BITKit
foreach (var hit in collider)
{
reportBuilder.AppendLine();
var top = hit.bounds.center + hit.bounds.extents;
if(top.y<sourceStartPosition.y)
{
continue;
}
reportBuilder.AppendLine();
reportBuilder.AppendLine($">{hit.name}");
if(top.y>groundReference.transform.position.y+maxHeight)
{
reportBuilder.AppendLine("高度超出可翻越高度");
continue;
}
var start = startPosition+forward*distance;
var start = sourceStartPosition+forward*8;
//start.y = hit.bounds.center.y;
var ray = new Ray(start, -forward);
@ -106,16 +137,35 @@ namespace BITKit
EndPosition = colliderHit.point + colliderHit.normal*0.4f;
EndPosition.y = top.y;
Debug.DrawLine(ray.origin, colliderHit.point, Color.green, 8);
var lineDistance = Vector3.Distance(start, colliderHit.point);
if(lineDistance > maxVaultDistance)
{
reportBuilder.AppendLine("长度超出可翻越距离");
BIT4Log.Log<GetVaultPointFromCollider>(reportBuilder.ToString());
try
{
foreach (var x in UnityEngine
.Physics
.OverlapSphere(EndPosition, 0.1f, layerMask))
{
if(Equals(x, hit))
continue;
throw new OperationCanceledException($"终点有其他碰撞体{x.name}");
}
}
catch (OperationCanceledException e)
{
reportBuilder.AppendLine(e.Message);
continue;
}
if(UnityEngine.Physics.Raycast(EndPosition,Vector3.down,out _,1.6f,layerMask) is false)
{
Debug.DrawRay(EndPosition, Vector3.down*1.6f, Color.red, 8);
reportBuilder.AppendLine("未检测到地面,跳过");
continue;
//Debug.DrawRay(EndPosition, Vector3.down, Color.red, 8);
}
var fixdPosition = colliderHit.point;
fixdPosition.y=hit.bounds.center.y;
var start2 = fixdPosition;
@ -126,7 +176,7 @@ namespace BITKit
{
reportBuilder.AppendLine($"检测到了障碍物{downHit.collider.name}");
BIT4Log.Log<GetVaultPointFromCollider>(reportBuilder.ToString());
//BIT4Log.Log<GetVaultPointFromCollider>(reportBuilder.ToString());
continue;
}
@ -145,6 +195,19 @@ namespace BITKit
StartPosition.y = top.y;
StartPosition += colliderHit.normal * 0.4f;
var closeStart = hit.ClosestPoint(StartPosition);
var closeEnd = hit.ClosestPoint(EndPosition);
var lineDistance = Vector3.Distance(closeStart, closeEnd);
if(lineDistance > maxVaultDistance)
{
reportBuilder.AppendLine($"长度{lineDistance}超出可翻越距离{maxVaultDistance}");
Debug.DrawLine(closeStart,closeEnd, Color.yellow, 4);
//BIT4Log.Log<GetVaultPointFromCollider>(reportBuilder.ToString());
continue;
}
vector3 = colliderHit.point;

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
using BITKit.Core.Tuple;
using UnityEngine;
namespace BITKit.Physics
namespace BITKit
{
[Serializable]
public class JointConfigure

View File

@ -1,10 +1,9 @@
using System.Collections;
using System.Collections.Generic;
using BITKit.Events;
using UnityEditor;
using UnityEngine;
namespace BITKit.Physics
namespace BITKit
{
public class Prop_Physics : MonoBehaviour
{

View File

@ -0,0 +1,3 @@
{
"name": "BITKit.Quadtree"
}

View File

@ -0,0 +1,11 @@
using Quadtree.Items;
using UnityEngine;
namespace Quadtree
{
[ExecuteInEditMode]
[AddComponentMenu("Spatial partitioning/Quadtree/Root node (for GameObjects)")]
public class GameObjectQuadtreeRoot : QuadtreeMonoRoot<GameObjectItem, Node<GameObjectItem>>
{
}
}

View File

@ -0,0 +1,118 @@
using Quadtree.Items;
using System.Collections.Generic;
using UnityEngine;
namespace Quadtree
{
/// <summary>
/// Mandatory interface of any single quadtree node.
/// </summary>
public interface INode<TItem, TNode>
where TItem : IItem<TItem, TNode>
where TNode : INode<TItem, TNode>
{
/// <summary>
/// Bounds of this tree node.
/// </summary>
Bounds Bounds { get; set; }
/// <summary>
/// Root of the whole tree.
/// </summary>
IQuadtreeRoot<TItem, TNode> TreeRoot { get; set; }
/// <summary>
/// Reference to parent tree node.
/// </summary>
/// <remarks>
/// Is <c>null</c> for root node of the tree.
/// </remarks>
TNode ParentNode { get; set; }
/// <summary>
/// Child nodes of this node.
/// </summary>
IList<TNode> SubNodes { get; set; }
/// <summary>
/// Verifies whether provided boundaries (<paramref name="bounds"/>) are fully contained within the boundaries of the node.
/// </summary>
///
/// <param name="bounds">Boundaries of an object</param>
/// <returns><c>True</c> if object is fully contained within the node, <c>False</c> otherwise</returns>
bool Contains(Bounds bounds);
/// <summary>
/// Calculates relative internal position of the provided bounds (<paramref name="bounds"/>) within the node.
/// </summary>
/// <remarks>
/// The method expects the boundaries to be fully contained within the node.
/// </remarks>
///
/// <param name="bounds">Boundaries contained within the node</param>
/// <returns>Relative internal position</returns>
IntraLocation Location(Bounds bounds);
/// <summary>
/// Inserts item (<paramref name="item"/>) into the smallest node possible in the subtree.
/// </summary>
/// <remarks>
/// The method expects item boundaries to be fully contained within the node.
/// </remarks>
///
/// <param name="item">Item to be inserted</param>
void Insert(TItem item);
/// <summary>
/// Removes the provided item (<paramref name="item"/>) from the node and its subtree.
/// </summary>
///
/// <param name="item">Item to be removed from the tree</param>
void Remove(TItem item);
/// <summary>
/// Checks whether the node and recursively all its subnodes are empty.
/// </summary>
///
/// <returns><c>True</c> if node and all its subnodes are empty, <c>False</c> otherwise</returns>
bool IsEmpty();
/// <summary>
/// Updates provided item's (<paramref name="item"/>) location within the tree.
/// </summary>
///
/// <param name="item">Item which's location is to be updated</param>
/// <param name="forceInsertionEvaluation"><c>True</c> forces tree to re-insert the item</param>
/// <param name="hasOriginallyContainedItem"><c>True</c> only for the first called node</param>
void Update(TItem item, bool forceInsertionEvaluation = true, bool hasOriginallyContainedItem = true);
/// <summary>
/// Finds items (<paramref name="items"/>) located within provided boundaries (<paramref name="bounds"/>).
/// </summary>
///
/// <param name="bounds">Boundaries to look for items within</param>
/// <param name="items">Output list for found items</param>
void FindAndAddItems(Bounds bounds, ref IList<TItem> items);
/// <summary>
/// Adds all items of this node and its sub-nodes to the provided list of items (<paramref name="items"/>).
/// If boundaries (<paramref name="bounds"/>) are provided then only items intersecting with them will be added.
/// </summary>
///
/// <param name="items">Output list for found items</param>
/// <param name="bounds">Boundaries to look for items within</param>
void AddItems(ref IList<TItem> items, Bounds? bounds = null);
/// <summary>
/// Removes any existing items from the node and removes all of its sub-nodes.
/// </summary>
void Clear();
/// <summary>
/// Displays boundaries of this node and all its sub-nodes and optinally a current number of contained items if <paramref name="displayNumberOfItems"/> is <c>True</c>.
/// </summary>
///
/// <param name="displayNumberOfItems"><c>True</c> if number of node's items should be displayed</param>
void DrawBounds(bool displayNumberOfItems = false);
}
}

View File

@ -0,0 +1,70 @@
using Quadtree.Items;
using System.Collections.Generic;
using UnityEngine;
namespace Quadtree
{
/// <summary>
/// Main class of the Quadtree structure - it represents the root of the tree.
/// </summary>
public interface IQuadtreeRoot<TItem, TNode>
where TItem : IItem<TItem, TNode>
where TNode : INode<TItem, TNode>
{
/// <summary>
/// The tree has been initialized and is ready to be used.
/// </summary>
bool Initialized { get; }
/// <summary>
/// Node currently acting as a root of the tree.
/// </summary>
TNode CurrentRootNode { get; }
/// <summary>
/// Minimum possible size of any of the nodes.
/// </summary>
/// <remarks>
/// Must always be a positive number or zero for no size limit.
/// </remarks>
float MinimumPossibleNodeSize { get; }
/// <summary>
/// Determines whether or not should number of items in nodes be displayed in gizmos.
/// </summary>
bool DisplayNumberOfItemsInGizmos { get; }
/// <summary>
/// Inserts item to the tree structure.
/// </summary>
///
/// <param name="item">Item to be inserted</param>
void Insert(TItem item);
/// <summary>
/// Expands size of root node.
/// New root node is created and current root node is assigned as its sub-node.
/// </summary>
void Expand();
/// <summary>
/// Finds items located within provided boundaries.
/// </summary>
///
/// <param name="bounds">Boundaries to look for items within</param>
/// <returns>List of items found within provided boundaries</returns>
List<TItem> Find(Bounds bounds);
/// <summary>
/// Removes provided item from the tree.
/// </summary>
///
/// <param name="item">Item to be removed from the tree</param>
void Remove(TItem item);
/// <summary>
/// Clears and resets the whole tree.
/// </summary>
void Clear();
}
}

View File

@ -0,0 +1,22 @@

namespace Quadtree
{
/// <summary>
/// Describes relative local position in respect to the current node.
/// </summary>
/// <remarks>
/// Integer values of <c>UPPER_LEFT</c>, <c>UPPER_RIGHT</c>, <c>LOWER_RIGHT</c>, <c>LOWER_LEFT</c> do correspond with the indices of the sub-nodes.
/// </remarks>
public enum IntraLocation
{
UPPER_LEFT,
UPPER_RIGHT,
LOWER_RIGHT,
LOWER_LEFT,
SPANNING_LEFT,
SPANNING_RIGHT,
SPANNING_UPPER,
SPANNING_LOWER,
SPANNING
};
}

View File

@ -0,0 +1,10 @@

using UnityEngine;
namespace Quadtree.Items
{
public abstract class GameObjectItem : GameObjectItemBase<GameObjectItem, Node<GameObjectItem>>
{
protected override GameObjectItem This() => this;
}
}

View File

@ -0,0 +1,152 @@
using UnityEngine;
namespace Quadtree.Items
{
/// <summary>
/// Custom item interface for GameObject quadtree items.
/// </summary>
public abstract class GameObjectItemBase<TItem, TNode> : MonoBehaviour, IItem<TItem, TNode>
where TItem : IItem<TItem, TNode>
where TNode : INode<TItem, TNode>
{
/// <summary>
/// Game object's bounds from last update call.
/// </summary>
private Bounds _lastBounds;
/// <summary>
/// Game object's bounds from last update call.
/// </summary>
private Bounds _safeBounds;
//==========================================================================dd==
// MonoBehaviour METHODS
//==========================================================================dd==
private void Start()
{
Init();
}
private void OnEnable()
{
Init();
}
private void OnDisable()
{
Root = null;
ItemInitialized = false;
ParentNode.Remove(This());
}
private void LateUpdate()
{
var currentBounds = GetBounds();
if (currentBounds != _lastBounds)
{
// the object has moved or changed size
var forceInsertionEvaluation = false;
if (!currentBounds.Intersects(_safeBounds)
|| (currentBounds.size - _lastBounds.size).magnitude > 0)
{
// ...far enough to force re-insertion
forceInsertionEvaluation = true;
_safeBounds = currentBounds;
}
// current object bounds are not the same as last update
// initiate tree update from currently
ParentNode?.Update(This(), forceInsertionEvaluation);
_lastBounds = currentBounds;
}
}
//==========================================================================dd==
// CORE TREE ITEM METHODS
//==========================================================================dd==
/// <summary>
/// <c>True</c> if the item has been initialized.
/// </summary>
protected internal bool ItemInitialized = false;
public IQuadtreeRoot<TItem, TNode> Root { get; set; }
public TNode ParentNode { get; set; }
public abstract Bounds GetBounds();
public void QuadTree_Root_Initialized(IQuadtreeRoot<TItem, TNode> root)
{
Root = root;
if (ItemInitialized)
{
// the item has been initialized before the tree root
root.Insert(This());
}
}
/// <summary>
/// Returns reference to corresponding game object.
/// </summary>
///
/// <returns>Game object instance.</returns>
public abstract GameObject GetGameObject();
/// <summary>
/// Initializes the item instance.
/// </summary>
/// <remarks>
/// This may be called either before or after the initialization of the tree root.
/// </remarks>
protected virtual void Init()
{
// designate item as initialized
ItemInitialized = true;
// set initial last bounds
_lastBounds = GetBounds();
// set initial safe bounds
_safeBounds = _lastBounds;
if (Root == null)
{
if (TryGetComponent(out GameObjectQuadtreeRoot quadtreeRoot) && quadtreeRoot.Initialized)
{
Root = (IQuadtreeRoot<TItem, TNode>)quadtreeRoot;
}
}
if (Root != null)
{
// the tree root has been initialized before the item
Root.Insert(This());
}
}
/// <summary>
/// Overloaded in sub-classes to return correct instance of the item.
/// </summary>
/// <remarks>
/// This method is necessary due to generic typechecking -- <c>this</c> in context of the abstract generic class does not reference TItem itself.
/// </remarks>
///
/// <returns>Instance of the item</returns>
protected abstract TItem This();
/// <summary>
/// Returns unique identifier of the item.
/// </summary>
/// <remarks>
/// It is extremely important to override this method because nodes are using HashSets to store items and the items are changing during the updates and so are the hash codes which can result in program not working properly.
/// </remarks>
///
/// <returns>Unique identifier</returns>
public override int GetHashCode()
{
return GetInstanceID();
}
}
}

View File

@ -0,0 +1,29 @@
using UnityEngine;
namespace Quadtree.Items
{
/// <summary>
/// Mandatory interface of any quadtree item.
/// </summary>
public interface IItem<TItem, TNode>
where TItem : IItem<TItem, TNode>
where TNode : INode<TItem, TNode>
{
/// <summary>
/// Returns object bounds.
/// </summary>
///
/// <returns>Object box bounds.</returns>
Bounds GetBounds();
/// <summary>
/// Node which currently contains the item.
/// </summary>
TNode ParentNode { get; set; }
/// <summary>
/// Receiver method for broadcasted tree initialization message.
/// </summary>
void QuadTree_Root_Initialized(IQuadtreeRoot<TItem, TNode> root);
}
}

View File

@ -0,0 +1,46 @@
using UnityEngine;
namespace Quadtree.Items
{
/// <summary>
/// Boundaries of this quadtree item are determined by present <c>UnityEngine.Renderer</c> component.
/// </summary>
[ExecuteInEditMode]
[DisallowMultipleComponent]
[RequireComponent(typeof(Renderer))]
[AddComponentMenu("Spatial partitioning/Quadtree/Items/Renderer-based Item")]
public class RendererItem : GameObjectItem
{
/// <summary>
/// Determines whether the item should be automatically inserted into the tree upon its initialization.
/// </summary>
///
/// <seealso cref="QuadtreeMonoRoot{TItem}.Insert(TItem)"/>
[SerializeField]
protected bool InsertOnInitialization = true;
private Renderer _renderer;
//==========================================================================dd==
// Quadtree ITEM METHODS
//==========================================================================dd==
/// <summary>
/// Finds and locally stores this <c>GameObject</c>'s <c>Renderer</c> component instance.
/// </summary>
protected override void Init()
{
// load game object renderer component
_renderer = GetComponent<Renderer>();
base.Init();
}
public override Bounds GetBounds()
{
return _renderer.bounds;
}
public override GameObject GetGameObject() => gameObject;
}
}

View File

@ -0,0 +1,12 @@
using Quadtree.Items;
namespace Quadtree
{
/// <summary>
/// Single quadtree node.
/// </summary>
public class Node<TItem> : NodeBase<TItem, Node<TItem>>
where TItem : IItem<TItem, Node<TItem>>
{
}
}

View File

@ -0,0 +1,416 @@
using Quadtree.Items;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
namespace Quadtree
{
/// <summary>
/// Base quadtree node implementation.
/// </summary>
public abstract class NodeBase<TItem, TNode> : INode<TItem, TNode>
where TItem : IItem<TItem, TNode>
where TNode : NodeBase<TItem, TNode>, new()
{
public Bounds Bounds { get; set; }
public TNode ParentNode { get; set; }
public IList<TNode> SubNodes { get; set; }
public IQuadtreeRoot<TItem, TNode> TreeRoot { get; set; }
/// <summary>
/// List of inserted items.
/// </summary>
private readonly HashSet<TItem> _items;
public NodeBase()
{
SubNodes = new List<TNode>(4);
_items = new HashSet<TItem>();
}
/// <summary>
/// Verifies whether provided boundaries (<paramref name="bounds"/>) are fully contained within the boundaries of the node.
/// </summary>
///
/// <param name="bounds">Boundaries of an object</param>
/// <returns><c>True</c> if object is fully contained within the node, <c>False</c> otherwise</returns>
public bool Contains(Bounds bounds) =>
bounds.min.x >= Bounds.min.x
&& bounds.min.z >= Bounds.min.z
&& bounds.max.x < Bounds.max.x
&& bounds.max.z < Bounds.max.z;
public IntraLocation Location(Bounds bounds)
{
if (bounds.min.z >= Bounds.center.z)
{
// items are located in top sub-nodes
if (bounds.max.x < Bounds.center.x)
{
// items are located in top left sub-node
return IntraLocation.UPPER_LEFT;
}
else if (bounds.min.x >= Bounds.center.x)
{
// items are located in top right sub-node
return IntraLocation.UPPER_RIGHT;
}
else
{
// item does not fit to either one, but is top
// (max.x is right, min.x is left)
return IntraLocation.SPANNING_UPPER;
}
}
else if (bounds.max.z < Bounds.center.z)
{
// items are located in bottom sub-nodes
if (bounds.max.x < Bounds.center.x)
{
// items are located in bottom left sub-node
return IntraLocation.LOWER_LEFT;
}
else if (bounds.min.x >= Bounds.center.x)
{
// items are located in bottom right sub-node
return IntraLocation.LOWER_RIGHT;
}
else
{
// item does not fit to either one, but is bottom
// (max.x is right, min.x is left)
return IntraLocation.SPANNING_LOWER;
}
}
else
{
// item does not fit to any sub-node
// (max.z is top, min.z is bottom)
if (bounds.min.x >= Bounds.center.x)
{
// bounds span over top right and bottom right nodes
return IntraLocation.SPANNING_RIGHT;
}
else if (bounds.max.x < Bounds.center.x)
{
// bounds span over top left and bottom left nodes
return IntraLocation.SPANNING_LEFT;
}
else
{
// bounds span over all sub-nodes
return IntraLocation.SPANNING;
}
}
}
public void Insert(TItem item)
{
// create new sub-nodes
if (SubNodes.Count == 0)
CreateSubNodes();
// sub-nodes can not be created anymore
if (SubNodes.Count == 0)
{
// insert item into this node
_items.Add(item);
// and designate this node its parent
item.ParentNode = (TNode)this;
return;
}
var itemBounds = item.GetBounds();
var itemBoundsLocation = Location(itemBounds);
switch (itemBoundsLocation)
{
// boundaries are contained within one of the subnodes
case IntraLocation.UPPER_LEFT:
case IntraLocation.UPPER_RIGHT:
case IntraLocation.LOWER_RIGHT:
case IntraLocation.LOWER_LEFT:
SubNodes[(int)itemBoundsLocation].Insert(item);
break;
// boundaries are spanning over 2 or more subnodes
default:
_items.Add(item);
item.ParentNode = (TNode)this;
break;
}
}
public void Remove(TItem item)
{
var itemBounds = item.GetBounds();
var itemBoundsLocation = Location(itemBounds);
switch (itemBoundsLocation)
{
// boundaries are contained within one of the subnodes
case IntraLocation.UPPER_LEFT:
case IntraLocation.UPPER_RIGHT:
case IntraLocation.LOWER_RIGHT:
case IntraLocation.LOWER_LEFT:
SubNodes[(int)itemBoundsLocation].Remove(item);
break;
// boundaries are spanning over 2 or more subnodes
default:
RemoveOwnItem(item);
break;
}
}
/// <summary>
/// Removes provided item (<paramref name="item"/>) from the node.
/// </summary>
///
/// <param name="item">Item to be removed from the node</param>
///
/// <seealso cref="INode{TItem, TNode}.Clear"/>
protected internal void RemoveOwnItem(TItem item)
{
// remove the item from the node
_items.Remove(item);
// update its parent node
item.ParentNode = null;
if (IsEmpty())
{
// remove subnodes if subtree of this node is empty
SubNodes.Clear();
}
}
public bool IsEmpty()
{
if (_items.Count > 0)
return false;
foreach (var subNode in SubNodes)
if (!subNode.IsEmpty())
return false;
return true;
}
public void Update(TItem item, bool forceInsertionEvaluation = true, bool hasOriginallyContainedItem = true)
{
if (Contains(item.GetBounds()))
{
// item is contained by this node
if (hasOriginallyContainedItem)
{
// ...and this node has originally contained the item
if (forceInsertionEvaluation)
{
// ...and insertion evaluation is forced
// this checks whether the item hasn't moved into any of the subnodes
RemoveOwnItem(item);
Insert(item);
}
// item is still contained by its original node, no action necessary
return;
}
// ...but this node is not its original container
// insert item either to this node or any of its children
Insert(item);
// update has been successful
return;
}
// the item is not contained by this node
if (ParentNode == null)
{
// ...and this node does not have any parent - the tree must be expanded
TreeRoot.Expand();
if (ParentNode == null)
{
// the expansion has failed for some reason
Debug.LogError("Tree root expansion failed for item " + item.ToString());
return;
}
}
// the item is not contained by this node
if (hasOriginallyContainedItem)
{
// ...and this node has originally contained the item - it must be removed
RemoveOwnItem(item);
}
// parent is (now) available
ParentNode.Update(item, forceInsertionEvaluation, false);
// the item is now contained by another node, update has been successful
}
/// <summary>
/// Creates sub-nodes for the node.
/// </summary>
protected internal void CreateSubNodes()
{
var subBoundsSize = Bounds.size * .5f;
if (subBoundsSize.x < TreeRoot.MinimumPossibleNodeSize
|| subBoundsSize.z < TreeRoot.MinimumPossibleNodeSize)
{
// new sub-node bounds are too small
return;
}
var centerOffset = subBoundsSize * .5f;
// top left node [-x +z]
centerOffset.x *= -1f;
SubNodes.Insert((int)IntraLocation.UPPER_LEFT, new TNode()
{
TreeRoot = TreeRoot,
ParentNode = (TNode)this,
Bounds = new Bounds(Bounds.center + centerOffset, subBoundsSize),
});
// top right node [+x +z]
centerOffset.x *= -1f;
SubNodes.Insert((int)IntraLocation.UPPER_RIGHT, new TNode()
{
TreeRoot = TreeRoot,
ParentNode = (TNode)this,
Bounds = new Bounds(Bounds.center + centerOffset, subBoundsSize),
});
// bottom right node [+x -z]
centerOffset.z *= -1f;
SubNodes.Insert((int)IntraLocation.LOWER_RIGHT, new TNode()
{
TreeRoot = TreeRoot,
ParentNode = (TNode)this,
Bounds = new Bounds(Bounds.center + centerOffset, subBoundsSize),
});
// bottom left node [-x -z]
centerOffset.x *= -1f;
SubNodes.Insert((int)IntraLocation.LOWER_LEFT, new TNode()
{
TreeRoot = TreeRoot,
ParentNode = (TNode)this,
Bounds = new Bounds(Bounds.center + centerOffset, subBoundsSize),
});
}
public void FindAndAddItems(Bounds bounds, ref IList<TItem> items)
{
if (SubNodes.Count == 0)
{
// no sub-nodes exist
AddOwnItems(ref items);
return;
}
// always add any items in this node intersecting with the boundaries
AddOwnItems(ref items, bounds);
var boundsLocation = Location(bounds);
switch (boundsLocation)
{
// boundaries are contained within one of the subnodes
case IntraLocation.UPPER_LEFT:
case IntraLocation.UPPER_RIGHT:
case IntraLocation.LOWER_RIGHT:
case IntraLocation.LOWER_LEFT:
SubNodes[(int)boundsLocation].FindAndAddItems(bounds, ref items);
break;
// boundaries are spanning over left subnodes
case IntraLocation.SPANNING_LEFT:
SubNodes[(int)IntraLocation.UPPER_LEFT].AddItems(ref items, bounds);
SubNodes[(int)IntraLocation.LOWER_LEFT].AddItems(ref items, bounds);
break;
// boundaries are spanning over right subnodes
case IntraLocation.SPANNING_RIGHT:
SubNodes[(int)IntraLocation.UPPER_RIGHT].AddItems(ref items, bounds);
SubNodes[(int)IntraLocation.LOWER_RIGHT].AddItems(ref items, bounds);
break;
// boundaries are spanning over upper subnodes
case IntraLocation.SPANNING_UPPER:
SubNodes[(int)IntraLocation.UPPER_LEFT].AddItems(ref items, bounds);
SubNodes[(int)IntraLocation.UPPER_RIGHT].AddItems(ref items, bounds);
break;
// boundaries are spanning over lower subnodes
case IntraLocation.SPANNING_LOWER:
SubNodes[(int)IntraLocation.LOWER_LEFT].AddItems(ref items, bounds);
SubNodes[(int)IntraLocation.LOWER_RIGHT].AddItems(ref items, bounds);
break;
// boundaries are spanning over all subnodes
case IntraLocation.SPANNING:
default:
AddSubNodeItems(ref items, bounds);
break;
}
}
public void AddItems(ref IList<TItem> items, Bounds? bounds = null)
{
AddOwnItems(ref items, bounds);
AddSubNodeItems(ref items, bounds);
}
/// <summary>
/// Adds all items belonging to this node (ignoring sub-nodes) to the provided list of items (<paramref name="items"/>).
/// If boundaries (<paramref name="bounds"/>) are provided then only items intersecting with them will be added.
/// </summary>
///
/// <param name="items">Output list for found items</param>
/// <param name="bounds">Boundaries to look for items within</param>
protected internal void AddOwnItems(ref IList<TItem> items, Bounds? bounds = null)
{
var itemSource = bounds != null
? _items.Where(item => item.GetBounds().Intersects((Bounds)bounds))
: _items;
foreach (var item in itemSource)
{
items.Add(item);
}
}
/// <summary>
/// Adds all items belonging to sub-nodes (ignoring own items) to the provided list of items (<paramref name="items"/>).
/// If boundaries (<paramref name="bounds"/>) are provided then only items intersecting with them will be added.
/// </summary>
///
/// <param name="items">Output list for found items</param>
/// <param name="bounds">Boundaries to look for items within</param>
protected internal void AddSubNodeItems(ref IList<TItem> items, Bounds? bounds = null)
{
foreach (var subNode in SubNodes)
subNode.AddItems(ref items, bounds);
}
public void Clear()
{
_items.Clear();
SubNodes.Clear();
}
public void DrawBounds(bool displayNumberOfItems = false)
{
if (displayNumberOfItems)
Handles.Label(Bounds.center, _items.Count.ToString());
Gizmos.DrawWireCube(Bounds.center, Bounds.size);
foreach (var subNode in SubNodes)
subNode.DrawBounds(displayNumberOfItems);
}
}
}

View File

@ -0,0 +1,102 @@
using Quadtree.Items;
using System.Collections.Generic;
using UnityEngine;
namespace Quadtree
{
/// <summary>
/// Main class of the Quadtree structure - it represents the root of the tree.
/// </summary>
public abstract class QuadtreeMonoRoot<TItem, TNode> : MonoBehaviour, IQuadtreeRoot<TItem, TNode>
where TItem : IItem<TItem, TNode>
where TNode : INode<TItem, TNode>, new()
{
//==========================================================================dd==
// MonoBehaviour METHODS
//==========================================================================dd==
protected void Start()
{
Init();
}
protected void OnEnable()
{
Init();
}
protected void OnDrawGizmos()
{
DrawBounds();
}
//==========================================================================dd==
// CORE ROOT NODE METHODS
//==========================================================================dd==
/// <summary>
/// Root node containing all items and sub-nodes.
/// </summary>
protected QuadtreeRoot<TItem, TNode> TreeRoot = null;
public bool Initialized => TreeRoot != null && TreeRoot.Initialized;
public TNode CurrentRootNode => TreeRoot.CurrentRootNode;
[SerializeField]
protected Vector3 DefaultRootNodeSize = new Vector3(64f, 0f, 64f);
/// <inheritdoc cref="IQuadtreeRoot{TItem, TNode}.MinimumPossibleNodeSize"/>
[SerializeField]
protected float MinimumPossibleNodeSize = 1f;
float IQuadtreeRoot<TItem, TNode>.MinimumPossibleNodeSize => MinimumPossibleNodeSize;
/// <inheritdoc cref="IQuadtreeRoot{TItem, TNode}.DisplayNumberOfItemsInGizmos"/>
[SerializeField]
private bool DisplayNumberOfItemsInGizmos = false;
bool IQuadtreeRoot<TItem, TNode>.DisplayNumberOfItemsInGizmos => DisplayNumberOfItemsInGizmos;
/// <summary>
/// Initializes Quadtree - creates initial root node and builds the tree (if allowed).
/// </summary>
///
/// <seealso cref="IItem{TItem, TNode}.QuadTree_Root_Initialized(IQuadtreeRoot{TItem, TNode})"/>
protected void Init()
{
if (TreeRoot == null)
{
TreeRoot = new QuadtreeRoot<TItem, TNode>(transform.position, DefaultRootNodeSize);
}
else
{
// root node has already been initialized, clear the tree
TreeRoot.Clear();
}
// send a message to children that the tree root has been initialized
BroadcastMessage("QuadTree_Root_Initialized", this, SendMessageOptions.DontRequireReceiver);
}
/// <summary>
/// Displays Quadtree node boundaries.
/// </summary>
///
/// <seealso cref="INode{TItem, TNode}.DrawBounds(bool)"/>
protected void DrawBounds()
{
TreeRoot?.CurrentRootNode.DrawBounds(DisplayNumberOfItemsInGizmos);
}
public void Insert(TItem item) => TreeRoot.Insert(item);
public void Expand() => TreeRoot.Expand();
public List<TItem> Find(Bounds bounds) => TreeRoot.Find(bounds);
public void Remove(TItem item) => TreeRoot.Remove(item);
public void Clear() => TreeRoot.Clear();
}
}

View File

@ -0,0 +1,148 @@
using Quadtree.Items;
using System.Collections.Generic;
using UnityEngine;
namespace Quadtree
{
/// <summary>
/// Main class of the Quadtree structure - it represents the root of the tree.
/// </summary>
public class QuadtreeRoot<TItem, TNode> : IQuadtreeRoot<TItem, TNode>
where TItem : IItem<TItem, TNode>
where TNode : INode<TItem, TNode>, new()
{
public bool Initialized { get; set; }
public TNode CurrentRootNode { get; internal set; }
public float MinimumPossibleNodeSize => _minimumPossibleNodeSize;
/// <inheritdoc cref="IQuadtreeRoot{TItem}.MinimumPossibleNodeSize"/>
protected float _minimumPossibleNodeSize = 1f;
public bool DisplayNumberOfItemsInGizmos => _displayNumberOfItemsInGizmos;
/// <inheritdoc cref="IQuadtreeRoot{TItem}.DisplayNumberOfItemsInGizmos"/>
protected bool _displayNumberOfItemsInGizmos = false;
/// <summary>
/// Determines side of root node expansion if necessary.
/// </summary>
protected bool ExpansionRight = true;
/// <summary>
/// Initializes Quadtree - creates initial root node and builds the tree (if allowed).
/// </summary>
public QuadtreeRoot(Vector3 center, Vector3 size)
{
CurrentRootNode = new TNode
{
TreeRoot = this,
ParentNode = default,
Bounds = new Bounds(center, size),
};
Initialized = true;
}
public void Insert(TItem item)
{
// get item bounds
var itemBounds = item.GetBounds();
// expand root node if necessary
while (!CurrentRootNode.Contains(itemBounds))
Expand();
// insert item into the tree
CurrentRootNode.Insert(item);
}
public void Expand()
{
// the subnodes will be of the same size as current root node
var subBoundsSize = CurrentRootNode.Bounds.size;
var centerOffset = subBoundsSize * .5f;
// center if expanding to left
var center = CurrentRootNode.Bounds.min;
if (ExpansionRight)
{
// center if expanding to right
center = CurrentRootNode.Bounds.max;
}
var subNodes = new List<TNode>(4);
var newRootNode = new TNode
{
TreeRoot = this,
ParentNode = default,
Bounds = new Bounds(center, subBoundsSize * 2f),
SubNodes = subNodes,
};
CurrentRootNode.ParentNode = newRootNode;
// top left node [-x +y]
centerOffset.x *= -1f;
subNodes.Insert((int)IntraLocation.UPPER_LEFT, new TNode
{
TreeRoot = this,
ParentNode = newRootNode,
Bounds = new Bounds(center + centerOffset, subBoundsSize),
});
// top right node [+x +y]
centerOffset.x *= -1f;
subNodes.Insert((int)IntraLocation.UPPER_RIGHT, !ExpansionRight
? CurrentRootNode
: new TNode
{
TreeRoot = this,
ParentNode = newRootNode,
Bounds = new Bounds(center + centerOffset, subBoundsSize),
});
// bottom right node [+x -y]
centerOffset.z *= -1f;
subNodes.Insert((int)IntraLocation.LOWER_RIGHT, new TNode
{
TreeRoot = this,
ParentNode = newRootNode,
Bounds = new Bounds(center + centerOffset, subBoundsSize),
});
// bottom left node [-x -y]
centerOffset.x *= -1f;
subNodes.Insert((int)IntraLocation.LOWER_LEFT, ExpansionRight
? CurrentRootNode
: new TNode
{
TreeRoot = this,
ParentNode = newRootNode,
Bounds = new Bounds(center + centerOffset, subBoundsSize),
});
// assign new root node
CurrentRootNode = newRootNode;
// toggle expansion side for next expansion
ExpansionRight = !ExpansionRight;
}
public List<TItem> Find(Bounds bounds)
{
IList<TItem> itemList = new List<TItem>();
CurrentRootNode.FindAndAddItems(bounds, ref itemList);
return (List<TItem>)itemList;
}
public void Remove(TItem item)
{
CurrentRootNode.Remove(item);
}
public void Clear()
{
CurrentRootNode.Clear();
}
}
}

View File

@ -0,0 +1,141 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using BITKit;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Jobs;
using UnityEngine.Pool;
namespace BITFALL.Rig{
public class TickOverrideTranformService : MonoBehaviour
{
//[BurstCompile]
// public struct CopyTransformJob : IJobParallelForTransform
// {
// [Unity.Collections.ReadOnly]
// public NativeArray<float3> positions;
// [Unity.Collections.ReadOnly]
// public NativeArray<quaternion> rotations;
//
//
// // The code actually running on the job
// public void Execute(int index, TransformAccess transform)
// {
// transform.SetPositionAndRotation(positions[index],rotations[index]);
// }
// }
public static void Register(int id,TickOverrideTransform tickOverrideTransform)
{
Dictionary.Add(id,tickOverrideTransform);
IsDirty = true;
}
public static void UnRegister(int id)
{
Dictionary.Remove(id);
IsDirty = true;
}
private static readonly Dictionary<int, TickOverrideTransform> Dictionary = new();
private static bool IsDirty;
private static Transform[] Sources;
private static Transform[] Targets;
[SerializeReference, SubclassSelector] private ITicker ticker;
// private TransformAccessArray m_AccessArray;
// private NativeArray<quaternion> _rotations;
// private NativeArray<float3> _positions;
// private JobHandle _jobHandle;
// private InitializationState _initializationState;
private void OnEnable()
{
ticker.Add(Tick);
}
private void OnDisable()
{
ticker.Remove(Tick);
}
// private void OnDestroy()
// {
// if (_initializationState is not InitializationState.Initializing) return;
// _jobHandle.Complete();
// _rotations.Dispose();
// _positions.Dispose();
// }
private void Tick(float obj)
{
// switch (_initializationState)
// {
// case InitializationState.Initializing when _jobHandle.IsCompleted:
// _jobHandle.Complete();
// _rotations.Dispose();
// _positions.Dispose();
// _initializationState = InitializationState.Initialized;
// break;
// case InitializationState.None:
// break;
// default:
// return;
// }
if (IsDirty)
{
var newLength = Dictionary.Count;
Sources = new Transform[newLength];
Targets = new Transform[newLength];
//_positions = new NativeArray<float3>(newLength, Allocator.Persistent);
//_rotations = new NativeArray<quaternion>(newLength, Allocator.Persistent);
var index = 0;
foreach (var x in Dictionary.Values)
{
Sources[index] = x.Source;
Targets[index] = x.Target;
index++;
}
}
var length =Sources?.Length ?? 0;
if(length is 0) return;
// m_AccessArray = new TransformAccessArray(length);
// m_AccessArray.SetTransforms(Sources);
for (var j = 0; j < length; j++)
{
Sources[j].SetPositionAndRotation(Targets[j].position,Targets[j].rotation);
}
// foreach (var x in Targets)
// {
// _positions[i] = x.position;
// _rotations[i] = x.rotation;
// i++;
// }
//
// foreach (var x in Sources)
// {
// x.position = _positions[i];
// x.rotation = _rotations[i];
// }
// var _job = new CopyTransformJob()
// {
// positions = _positions,
// rotations = _rotations
// };
// _jobHandle = _job.Schedule(m_AccessArray);
//
// _initializationState = InitializationState.Initializing;
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITFALL.Rig
{
public class TickOverrideTransform : MonoBehaviour
{
public Transform Source;
public Transform Target;
private int Id;
private void OnEnable()
{
Id = GetInstanceID();
TickOverrideTranformService.Register(Id,this);
}
private void OnDisable()
{
TickOverrideTranformService.UnRegister(Id);
}
#if UNITY_EDITOR
private void OnValidate()
{
if(!Source)Source = transform;
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
}
}

View File

@ -41,6 +41,15 @@ namespace BITKit.SceneManagement
/// 场景加载完成的回调
/// </summary>
event Action<string> OnSceneLoaded;
/// <summary>
/// 注册加载任务
/// </summary>
void RegisterLoadTaskAsync(Func<UniTask> task);
/// <summary>
/// 注销加载任务
/// </summary>
/// <param name="task"></param>
void UnRegisterLoadTaskAsync(Func<UniTask> task);
/// <summary>
/// 当开始卸载场景时
/// </summary>
@ -89,6 +98,16 @@ namespace BITKit.SceneManagement
remove => _sceneServiceImplementation.OnSceneLoaded -= value;
}
public void RegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.RegisterLoadTaskAsync(task);
}
public void UnRegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.UnRegisterLoadTaskAsync(task);
}
public event Action<string> OnUnloadScene
{
add => _sceneServiceImplementation1.OnUnloadScene += value;

View File

@ -1,10 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
namespace BITKit
{
@ -20,6 +22,8 @@ namespace BITKit
private Label _titleLabel;
private VisualElement _container;
private MeshRenderer[] _renderers=Array.Empty<MeshRenderer>();
private void OnEnable()
{
UnityEditor.Selection.selectionChanged += OnSelectionChanged;
@ -38,22 +42,52 @@ namespace BITKit
private void OnSelectionChanged()
{
var active = UnityEditor.Selection.activeGameObject;
var actives = UnityEditor.Selection.gameObjects;
_container.Clear();
_titleLabel.text = active ? active.name : "未选择物体";
if (!active) return;
foreach (var x in active.GetComponentsInChildren<MeshRenderer>(true))
_renderers = actives.SelectMany(x => x.GetComponentsInChildren<MeshRenderer>()).ToArray();
var materials = _renderers.SelectMany(x => x.sharedMaterials).Distinct().ToArray();
_titleLabel.text = actives is not {Length:0} ?$"选择了{actives.Length }个物体,{materials.Length}个材质" : "未选择物体";
foreach (var material in materials)
{
foreach (var material in x.sharedMaterials)
var filed = _container.Create<ObjectField>();
filed.label = material.name;
filed.objectType = typeof(Material);
filed.allowSceneObjects = false;
filed.value = material;
filed.RegisterValueChangedCallback(OnChanged);
}
}
private void OnChanged(ChangeEvent<Object> evt)
{
if(evt.newValue is not Material value) return;
var list = new List<Object>();
foreach (var renderer in _renderers)
{
var sharedMaterials = renderer.sharedMaterials;
var isDirty = false;
for (var i = 0; i < sharedMaterials.Length; i++)
{
var filed = rootVisualElement.Create<ObjectField>();
filed.label = material.name;
filed.objectType = typeof(Material);
filed.value = material;
var current = sharedMaterials[i];
if(current != evt.previousValue) continue;
sharedMaterials[i] = value;
isDirty = true;
}
if (!isDirty) continue;
renderer.sharedMaterials = sharedMaterials;
list.Add(renderer);
EditorUtility.SetDirty(renderer);
}
if (list.Count > 0)
{
OnSelectionChanged();
}
}
}

View File

@ -58,6 +58,16 @@ namespace BITKit.SceneManagement
remove => SceneService.OnSceneLoaded -= value;
}
public void RegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.RegisterLoadTaskAsync(task);
}
public void UnRegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.UnRegisterLoadTaskAsync(task);
}
public event Action<string> OnUnloadScene
{
add => _sceneServiceImplementation.OnUnloadScene += value;
@ -198,9 +208,15 @@ namespace BITKit.SceneManagement
OnSceneLoadProgress?.Invoke(sceneName, progress);
}
LoadedObjects.Add(sceneName, handle.SceneObject);
OnSceneLoadProgress?.Invoke(sceneName, 1);
await Task.Delay(384, cancellationToken);
foreach (var x in _onSceneLoadedAsyncList.ToArray())
{
await x.Invoke();
if (destroyCancellationToken.IsCancellationRequested) return;
}
OnSceneLoaded?.Invoke(sceneName);
stopwatchWatcher.Stop();
// if (activateOnLoad is false)
@ -252,6 +268,16 @@ namespace BITKit.SceneManagement
remove => OnSceneLoaded -= value;
}
private readonly List<Func<UniTask>> _onSceneLoadedAsyncList=new();
public void RegisterLoadTaskAsync(Func<UniTask> task)
{
_onSceneLoadedAsyncList.Add(task);
}
public void UnRegisterLoadTaskAsync(Func<UniTask> task)
{
_onSceneLoadedAsyncList.Remove(task);
}
public event Action<string> OnUnloadScene;
public event Action<string> OnSceneUnloaded;
}

View File

@ -1,49 +1,49 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Pool;
using System.Linq;
using UnityEditor.Search;
namespace BITKit.Sensors
{
public interface IAudioObject
{
float GetVolume();
}
public class AudioSensor : Sensor
public class AudioSensor : MonoBehaviour,ISensor
{
[Header(Constant.Header.Settings)]
public float radius;
[Header(Constant.Header.InternalVariables)]
IAudioObject currentAudioObject;
Collider currentCollider;
Collider[] colliders = new Collider[32];
public override IEnumerable<Transform> Get() => detected;
public override UniTask Execute()
[SerializeField] private bool autoUpdate;
[SerializeField]private float radius;
private readonly CacheList<Transform> cache = new();
private void OnEnable()
{
var cacheList = ListPool<Transform>.Get();
for (int i = 0; i < Physics.OverlapSphereNonAlloc(transform.position, radius, colliders, detectLayer); i++)
Id = GetInstanceID();
SensorQueue.Register(Id,this);
}
private void OnDisable()
{
SensorQueue.UnRegister(Id);
}
public UniTask Execute(float delta)
{
var position = transform.position;
cache.Clear();
foreach (var x in AudioSensorService.QuadtreeRoot.Find(new Bounds(position, Vector3.one * radius)))
{
currentCollider = colliders[i];
if (IsValid(currentCollider))
{
cacheList.Add(currentCollider.transform);
}
var distance = Vector3.Distance(position, x.Position);
if(distance>radius) continue;
cache.Add(x.Transform);
}
detected = cacheList.ToArray();
ListPool<Transform>.Release(cacheList);
return UniTask.CompletedTask;
}
public override bool IsValid(Collider _collider)
{
if (ignoreColliders.Contains(_collider) is false)
if (Vector3.Distance(transform.position, _collider.transform.position) <= radius)
if (_collider.TryGetComponent<IAudioObject>(out currentAudioObject))
{
return currentAudioObject.GetVolume() >= 1;
}
return false;
}
public override float GetDistance() => radius;
public int Id { get; set; }
public IEnumerable<Transform> Get() => cache.ValueArray;
public bool IsValid(Collider _collider) => false;
public float GetDistance() => radius;
public bool AutoUpdate=>autoUpdate;
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using kcp2k;
using Quadtree;
using Quadtree.Items;
using UnityEngine;
using UnityEngine.Pool;
namespace BITKit.Sensors
{
public class AudioSensorService : MonoBehaviour
{
public class AudioSensorData:IItem<AudioSensorData,Node<AudioSensorData>>
{
public int Id;
public Vector3 Position;
public float Radius;
public Transform Transform;
public Bounds Bounds;
public ITag Tag;
public Bounds GetBounds() => Bounds;
public Node<AudioSensorData> ParentNode { get; set; }
public void QuadTree_Root_Initialized(IQuadtreeRoot<AudioSensorData, Node<AudioSensorData>> root){}
}
internal static readonly QuadtreeRoot<AudioSensorData, Node<AudioSensorData>> QuadtreeRoot =
new(default, Vector3.one * 2048);
private static Pool<AudioSensorData> pool = new(()=>new(), x=>{}, 1000);
private static int count;
public static async void MakeNoise(Vector3 position,Transform transform)
{
var data = pool.Take();
data.Id = count++;
data.Position = position;
data.Transform = transform;
data.Bounds = new Bounds(position, Vector3.one *1);
data.Tag = transform.GetComponent<ITag>();
QuadtreeRoot.Insert(data);
await UniTask.Delay(3000);
if (disposed) return;
QuadtreeRoot.Remove(data);
pool.Return(data);
}
private static bool disposed;
[SerializeReference, SubclassSelector] private ITicker ticker;
private void Start()
{
disposed = false;
ticker.Add(OnTick);
destroyCancellationToken.Register(Dispose);
pool.Clear();
}
private void Dispose()
{
ticker.Remove(OnTick);
disposed = true;
QuadtreeRoot.Clear();
}
private void OnTick(float obj)
{
}
}
}

View File

@ -9,7 +9,8 @@
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:be17a8778dbfe454890ed8279279e153",
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:9400d40641bab5b4a9702f65bf5c6eb5"
"GUID:9400d40641bab5b4a9702f65bf5c6eb5",
"GUID:1193c2664d97cc049a6e4c486c6bce71"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -17,6 +17,7 @@ namespace BITKit.Sensors
/// </summary>
public interface ISensor
{
int Id { get; }
/// <summary>
/// 自动更新
/// </summary>
@ -45,7 +46,7 @@ namespace BITKit.Sensors
/// 传感器执行检测
/// </summary>
/// <returns></returns>
UniTask Execute();
UniTask Execute(float delta);
}
[Serializable]
@ -53,6 +54,8 @@ namespace BITKit.Sensors
{
[SerializeField] private GameObject gameObject;
private ISensor _sensorImplementation => gameObject.GetComponent<ISensor>();
public int Id => _sensorImplementation.Id;
public IEnumerable<Transform> Get()
{
return _sensorImplementation.Get();
@ -67,10 +70,9 @@ namespace BITKit.Sensors
{
return _sensorImplementation.GetDistance();
}
public UniTask Execute()
public UniTask Execute(float delta)
{
return _sensorImplementation.Execute();
return _sensorImplementation.Execute(delta);
}
}
[System.Serializable]
@ -78,6 +80,8 @@ namespace BITKit.Sensors
{
[SerializeField] private MonoBehaviour monoBehaviour;
private ISensor _sensorImplementation=>monoBehaviour as ISensor;
public int Id => _sensorImplementation.Id;
public IEnumerable<Transform> Get()
{
return _sensorImplementation.Get();
@ -93,9 +97,9 @@ namespace BITKit.Sensors
return _sensorImplementation.GetDistance();
}
public UniTask Execute()
public UniTask Execute(float delta)
{
return _sensorImplementation.Execute();
return _sensorImplementation.Execute(delta);
}
}
public abstract class Sensor : MonoBehaviour, ISensor
@ -106,16 +110,15 @@ namespace BITKit.Sensors
[Header(Constant.Header.Gameobjects)]
public Collider[] ignoreColliders;
[Header(Constant.Header.InternalVariables)]
[NonSerialized]
[SerializeField,ReadOnly]
public Transform[] detected = Array.Empty<Transform>();
public abstract IEnumerable<Transform> Get();
public abstract bool IsValid(Collider _collider);
public abstract UniTask Execute();
public abstract float GetDistance();
public virtual UniTask Execute(float delta)=>UniTask.CompletedTask;
public int Id { get; private set; }
bool ISensor.AutoUpdate => autoUpdate;
protected int Id;
protected Transform Transform;
protected virtual void OnEnable()

View File

@ -0,0 +1,16 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.Sensors
{
public interface ISensorTarget
{
public int Id { get; }
Bounds Bounds { get; }
Transform Transform { get; }
void Detected(float weight,ISensor sensor,object sender);
event Action<float, ISensor, object> OnDetected;
}
}

View File

@ -12,7 +12,7 @@ namespace BITKit.Sensors
public override IEnumerable<Transform> Get()=>sensors.SelectMany(x => x.Get());
public override bool IsValid(Collider _collider) => sensors.Any(x => x.IsValid(_collider) is false) is false;
public override float GetDistance() => sensors.Min(x => x.GetDistance());
public override UniTask Execute() => UniTask.WhenAll(sensors.Select(x => x.Execute()));
public override UniTask Execute(float delta) => UniTask.WhenAll(sensors.Select(x => x.Execute(delta)));
}
}

View File

@ -7,24 +7,25 @@ using Cysharp.Threading.Tasks;
using UnityEngine.Jobs;
using UnityEngine.Pool;
using UnityEngine.Profiling;
using Physics=UnityEngine.Physics;
namespace BITKit.Sensors
{
public class RangeSensor : Sensor
{
[Header(Constant.Header.Settings)]
public float radius;
[Header(Constant.Header.Settings)] public float radius;
public int fov;
public bool requireSight;
[Header(Constant.Header.Settings)]
public LayerMask blockLayer;
[Header(Constant.Header.InternalVariables)]
private FrameUpdate frameUpdater;
private readonly Collider[] colliders = new Collider[32];
private RaycastHit[] hits;
[Header(Constant.Header.Settings)] public LayerMask blockLayer;
[Header(Constant.Header.Debug)] [Header(Constant.Header.InternalVariables)]
private FrameUpdate frameUpdater;
private readonly Collider[] colliders = new Collider[32];
private RaycastHit[] hits = new RaycastHit[32];
private readonly HashSet<int> tempHashSet = new();
private bool isExecuting;
public override IEnumerable<Transform> Get()
{
if (!_detectedDoubleBuffer.TryGetRelease(out var newRelease)) return _detectedBuffer;
@ -33,12 +34,13 @@ namespace BITKit.Sensors
Profiler.EndSample();
return _detectedBuffer;
}
private readonly DoubleBuffer<IEnumerable<Transform>> _detectedDoubleBuffer=new();
private IEnumerable<Transform> _detectedBuffer;
private readonly DoubleBuffer<IEnumerable<Transform>> _detectedDoubleBuffer = new();
private IEnumerable<Transform> _detectedBuffer=Array.Empty<Transform>();
public override UniTask Execute()
public override UniTask Execute(float delta)
{
tempHashSet.Clear();
var length = Physics.OverlapSphereNonAlloc(Transform.position, radius, colliders, detectLayer);
Profiler.BeginSample("Filter Detected Colliders");
var _newDetected = from x in colliders.Take(length) where IsValid(x) select x.transform;
@ -54,7 +56,7 @@ namespace BITKit.Sensors
case var _ when ignoreColliders.Contains(_collider):
return false;
case var _ when fov > 0 && CheckFov(ref _collider) is false:
case var _ when requireSight && CheckSight(ref _collider) is false:
case var _ when requireSight && CheckSight(ref _collider) is false:
return false;
default:
return true;
@ -62,18 +64,21 @@ namespace BITKit.Sensors
}
public override float GetDistance() => radius;
private bool CheckFov(ref Collider _collider)
{
var _dir = _collider.transform.position - transform.position;
if (_dir.sqrMagnitude <= 0) return false;
var dir = Quaternion.LookRotation(_dir);
return Vector3.Dot(transform.forward, _dir) > 0 && fov > Quaternion.Angle(transform.rotation, dir);
var _dir = _collider.bounds.center - transform.position;
if (_dir.sqrMagnitude <= 0) return false;
var dir = Quaternion.LookRotation(_dir);
return Vector3.Dot(transform.forward, _dir) > 0 && fov > Quaternion.Angle(transform.rotation, dir);
}
private bool CheckSight(ref Collider _collider)
{
if (!requireSight) return false;
var transform1 = _collider.transform;
var position = transform1.position;
var position = _collider.bounds.center;
//是检测bounds的顶部
position.y += _collider.bounds.extents.y;
var location = new Location(Transform);
var length = Physics.RaycastNonAlloc(
location.position,
@ -82,23 +87,32 @@ namespace BITKit.Sensors
Vector3.Distance(location, position),
blockLayer
);
switch (length)
{
case 0:
Debug.DrawLine(location, position, Color.green, 1);
return true;
case 1:
if (hits[0].collider == _collider)
{
return true;
}
break;
default:
if (hits.Take(length).Any(x => ignoreColliders.Contains(x.collider) is false))
return hits[0].collider == _collider;
default:
var collider1 = _collider;
if (hits.Take(length).Any(Predicate))
{
return false;
}
break;
bool Predicate(RaycastHit x)
{
var result = ignoreColliders.Contains(x.collider) is false && x.collider != collider1;
if (result)
{
Debug.DrawLine(location, x.point, Color.red, 1);
}
return result;
}
}
return true;
}
}

View File

@ -13,7 +13,7 @@ namespace BITKit.Sensors
public float distance;
public override IEnumerable<Transform> Get() => detected;
private readonly RaycastHit[] raycasts = new RaycastHit[16];
public override UniTask Execute()
public override UniTask Execute(float delta)
{
var _transform = transform;
var forward = _transform.forward;
@ -46,7 +46,7 @@ namespace BITKit.Sensors
{
if (autoUpdate)
{
Execute().Forget();
Execute(Time.deltaTime).Forget();
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
@ -10,6 +11,7 @@ namespace BITKit.Sensors
public class SensorQueue : MonoBehaviour
{
internal static readonly Dictionary<int,ISensor> Sensors=new();
internal static readonly ConcurrentDictionary<int, float> LastDetectedTime = new();
private static bool IsDirty;
[SerializeField,ReadOnly] private int _position;
@ -32,10 +34,25 @@ namespace BITKit.Sensors
}
[SerializeField] private MonoBehaviour[] sensors;
[SerializeReference,SubclassSelector] private ITicker ticker;
private void Update()
private bool _isBusy;
private void Start()
{
ticker.Add(OnTick);
destroyCancellationToken.Register(Dispose);
}
private void Dispose()
{
ticker.Remove(OnTick);
}
private async void OnTick(float obj)
{
if (_isBusy) return;
if (SensorGlobalSettings.Enabled is false) return;
_isBusy = true;
if(IsDirty)
{
_position = 0;
@ -44,11 +61,25 @@ namespace BITKit.Sensors
sensors = Sensors.Values.Where(IsEnabled).OfType<MonoBehaviour>().ToArray();
}
if(Sensors.Count is 0) return;
if (Sensors.Count is 0)
{
_isBusy = false;
return;
}
Sensors.ElementAt(_position++).Value.Execute().Forget();
var current = Sensors.ElementAt(_position++).Value;
var currentUpdateTime = LastDetectedTime.GetOrAdd(current.Id,Time.time);
await current.Execute(Time.time-currentUpdateTime);
float UpdateValueFactory(int key, float old) => Time.time;
LastDetectedTime.AddOrUpdate(current.Id,Time.time,UpdateValueFactory);
if (destroyCancellationToken.IsCancellationRequested) {
_isBusy = false;
return;
}
_position %= Sensors.Count;
_isBusy = false;
}
private bool IsEnabled(ISensor sensor)
{

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using BITKit.Sensors.States;
using BITKit.StateMachine;
using Cysharp.Threading.Tasks;
#if UNITY_EDITOR
using UnityEditor;
@ -16,212 +18,38 @@ using UnityEngine.UIElements;
namespace BITKit.Sensors
{
public interface ISmartTargetProperty{}
public interface ISmartTargetState:IState{}
/// <summary>
/// 智能目标传感器,根据条件,智能选择目标
/// </summary>
public class SmartTargetSensor :MonoBehaviour,ISensor,IAction
public class SmartTargetSensor :StateBasedMonoBehaviour<ISmartTargetState>,ISensor
{
/// <summary>
/// 自动更新
/// </summary>
[Header(Constant.Header.Settings)]
[SerializeField] private bool autoUpdate;
[SerializeField] private Optional<string[]> ignoreTags;
[SerializeField] private Optional<IntervalUpdate> optionalRetargetInterval;
/// <summary>
/// 主传感器
/// </summary>
[Header(nameof(Sensor))]
[SerializeField,SerializeReference,SubclassSelector] private ISensor sensor;
[Header(Constant.Header.Debug)]
[SerializeReference, ReadOnly] private int updateCount;
public IEnumerable<Transform> Get() =>CurrentTarget is not null ? new[] { CurrentTarget }:Enumerable.Empty<Transform>();
bool ISensor.AutoUpdate => autoUpdate;
internal StringBuilder reportBuilder;
internal DoubleBuffer<string> report=new();
public bool IsValid(Collider _collider)
{
if (!_collider) return false;
if (ignoreTags.Allow)
{
if (_collider.TryGetComponent<ITag>(out var iTags))
{
var tags = iTags.GetTags();
foreach (var x in ignoreTags.Value)
{
if (tags.Contains(x))
{
return false;
}
}
}
}
return true;
}
public float GetDistance() => sensor.GetDistance();
public Transform CurrentTarget { get; private set; }
private IEnumerable<Transform> detected = ArraySegment<Transform>.Empty;
private Vector3 _currentPosition;
private int Id;
private CancellationTokenSource timeoutCancellationTokenSource=new();
[SerializeField] private float radius;
[SerializeField] private RangeSensor rangeSensor;
[SerializeField] private AudioSensor audioSensor;
public int Id { get; set; }
private readonly CacheList<Transform> _detected=new();
private void OnEnable()
{
SensorQueue.Register(Id=GetInstanceID(),this);
Id = GetInstanceID();
SensorQueue.Register(Id,this);
}
private void Start()
{
TransitionState<Idle>();
}
private void OnDisable()
{
SensorQueue.UnRegister(Id);
}
public async UniTask Execute()
public IEnumerable<Transform> Get() => _detected.ValueArray;
public bool IsValid(Collider _collider) => false;
public float GetDistance() => radius;
public UniTask Execute(float delta)
{
try
{
_currentPosition = transform.position;
updateCount++;
timeoutCancellationTokenSource?.Cancel();
timeoutCancellationTokenSource = new CancellationTokenSource();
await sensor.Execute();
reportBuilder?.AppendLine($"-----BEGIN------{updateCount}");
Profiler.BeginSample("Release Detected Buffer");
var newDetected = sensor.Get();
if (reportBuilder is not null)
{
reportBuilder.AppendLine($"Detected:{newDetected.Count()}");
foreach (var x in newDetected)
{
reportBuilder.AppendLine(x.name);
}
reportBuilder.AppendLine();
}
Profiler.EndSample();
// ReSharper disable once PossibleMultipleEnumeration
if (newDetected.Contains(CurrentTarget))
{
if (optionalRetargetInterval.Allow && optionalRetargetInterval.Value.AllowUpdate)
{
reportBuilder?.AppendLine("Retarget Interval Catch,Search New Target");
}
else
{
reportBuilder?.AppendLine("Current Target Detected,Keep Target");
return;
}
}
Profiler.BeginSample("Filter Detected");
foreach (var x in newDetected.OrderBy(KeySelector))
{
if(IsValid(x.GetComponent<Collider>()) is false)continue;
CurrentTarget = x;
reportBuilder?.AppendLine($"New Target:{x.name},Is oder by distance");
break;
}
Profiler.EndSample();
reportBuilder?.AppendLine($"-----Complete------{updateCount}");
if(reportBuilder is not null)
{
report.Release(reportBuilder.ToString());
reportBuilder.Clear();
}
else
{
report.Clear();
}
}
catch (Exception e)
{
BIT4Log.LogException(e);
}
}
private float KeySelector(Transform x)
{
var distance = Vector3.Distance(_currentPosition, x.position);
reportBuilder?.AppendLine($"Distance:{distance}@{x.name}");
return distance;
}
void IAction.Execute()
{
Execute().Forget();
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
try
{
if (!CurrentTarget) return;
Gizmos.DrawLine(transform.position,CurrentTarget.position);
}
catch (UnassignedReferenceException)
{
}
}
#endif
}
#if UNITY_EDITOR
[CustomEditor(typeof(SmartTargetSensor))]
public class SmartTargetSensorInspector:BITInspector<SmartTargetSensor>
{
private ObjectField _objectField;
private Label _reportLabel;
public override VisualElement CreateInspectorGUI()
{
FillDefaultInspector();
CreateSubTitle("Editor Debug Field");
_objectField = root.Create<ObjectField>();
_objectField.objectType = typeof(Transform);
_objectField.SetEnabled(false);
_reportLabel = root.Create<Label>();
_reportLabel.text = "Waiting agent report";
return root;
}
protected override void OnEnabled()
{
base.OnEnabled();
agent.reportBuilder = new();
}
protected override void OnDisabled()
{
base.OnDisabled();
agent.reportBuilder = null;
}
protected override void OnUpdate()
{
if (_objectField is not null)
{
_objectField.value = agent.CurrentTarget;
}
if (agent.reportBuilder is not null && _reportLabel is not null && agent.report.TryGetRelease(out var value))
{
_reportLabel.text = value;
}
CurrentState?.OnStateUpdate(delta);
return UniTask.CompletedTask;
}
}
#endif
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit.StateMachine;
using UnityEngine;
namespace BITKit.Sensors.States
{
public abstract class SmartTargetSensorStates : ISmartTargetState
{
public 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)
{
}
}
[Serializable]
public sealed class Idle : SmartTargetSensorStates
{
}
}

View File

@ -61,6 +61,8 @@ namespace BITKit.Sensors
onLost.Invoke(_collider);
}
}
public int Id => _id is 0 ? _id = GetInstanceID() : _id;
private int _id;
public IEnumerable<Transform> Get()
{
@ -89,13 +91,11 @@ namespace BITKit.Sensors
if (ignores.Contains(detectedObject)) return false;
return !detectedLayer.Allow || detectedLayer.Value.Includes(_collider.gameObject.layer);
}
public float GetDistance()
{
throw new NotImplementedException();
}
public UniTask Execute()=>
public UniTask Execute(float delta = 0)=>
UniTask.CompletedTask;
private void Update()

View File

@ -51,10 +51,10 @@ namespace BITKit.StateMachine
state.Initialize();
StateDictionary.Add(state.GetType(), state);
}
if (states.Count > 0)
{
TransitionState(states[0]);
}
// if (states.Count > 0)
// {
// TransitionState(states[0]);
// }
}
public void UpdateState(float deltaTime)
{
@ -104,9 +104,8 @@ namespace BITKit.StateMachine
{
BIT4Log.Log<MonoStateMachine<TState>>($"TransitionState from {_currentStateName} to {newState.GetType().Name}");
}
newState.OnStateEntry(oldState);
CurrentState = newState;
newState.OnStateEntry(oldState);
OnStateChanged?.Invoke(oldState, newState);
_currentStateName = newState.GetType().Name;
newState.Enabled = true;

View File

@ -13,8 +13,14 @@ namespace BITKit
[SerializeField] private string[] tags;
[Tooltip("Disable when tags is not empty")]
[SerializeReference,SubclassSelector] private IReference[] reference;
public int Hash => _id is 0 ? _id = MathE.GetHash(GetTags()) : _id;
private int _id;
public string[] GetTags() => CacheTags ??= reference?.Length > 0 ? reference.Select(x => x.Value).ToArray() : tags;
private string[] CacheTags;
public void SetTags(IReference[] newReference)
{
reference = newReference;
CacheTags = null;
}
}
}

View File

@ -3,6 +3,9 @@ using System.Collections;
using System.Collections.Generic;
using System.Timers;
using Cysharp.Threading.Tasks;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace BITKit
@ -71,9 +74,16 @@ namespace BITKit
TickCount++;
try
{
var delta = (float)(BITApp.Time.TimeAsDouble - _lastTime);
_lastTime = BITApp.Time.TimeAsDouble;
if (isMainThread) await UniTask.SwitchToMainThread(destroyCancellationToken);
#if UNITY_EDITOR
if (EditorApplication.isPlaying is false)
{
Restart();
}
#endif
while (_ActionQueue.TryDequeue(out var action))
{
action?.Invoke();
@ -95,10 +105,16 @@ _TickEvents?.Invoke(delta);
BIT4Log.LogException(exception);
}
if (isConcurrent is false && destroyCancellationToken.IsCancellationRequested is false)
Restart();
return;
void Restart()
{
_timer.Start();
if (isConcurrent is false && destroyCancellationToken.IsCancellationRequested is false)
{
_timer.Start();
}
}
}
}
}

View File

@ -5,6 +5,9 @@ using System.Collections.Generic;
using System.Timers;
using Cysharp.Threading.Tasks;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace BITKit
{
@ -48,11 +51,23 @@ namespace BITKit
{
public event Action<float> Tick;
public float Interval { get; set; }
public async void Invoke(object sender, ElapsedEventArgs args)
{
await UniTask.SwitchToMainThread();
Tick?.Invoke(Interval);
#if UNITY_EDITOR
if (EditorApplication.isPlaying is false || EditorApplication.isPaused)
{
return;
}
#endif
try
{
Tick?.Invoke(Interval);
}
catch (Exception e)
{
BIT4Log.LogException(e);
}
}
}
public static void Add(Action<float> action, float interval)
@ -81,7 +96,6 @@ namespace BITKit
{
Interval = totalMilliseconds,
};
timer.AutoReset = true;
_CreateTimes.TryAdd(interval, GameTickService.TickCount);
var action = _Actions.GetOrAdd(interval, CreateAction);
@ -91,6 +105,13 @@ namespace BITKit
BIT4Log.Log<IntervalTickService>($"已创建Tick,Interval[{totalMilliseconds}]");
return timer;
void Tick(object sender, ElapsedEventArgs args)
{
action.Invoke(sender, args);
if (timer.Enabled)
timer.Start();
}
}
private static DelegateWrapper CreateAction(float interval)=>new()
{

View File

@ -101,7 +101,7 @@ namespace BITKit.UX
[UXBindPath(UXConstant.TitleLabel)]
private Label _titleLabel;
[UXBindPath(UXConstant.ContextLabel)]
private Label _contextLabel;
private TextElement _contextLabel;
[UXBindPath(UXConstant.MainButton)]
private Button _comfirmButton;
[UXBindPath(UXConstant.SecButton)]

View File

@ -41,8 +41,14 @@ public class UXBuilder : MonoBehaviour
public UXContainer BuildAsContainer(Func<VisualElement> createFactory) => new(Build<VisualElement>(createFactory));
public UXContainer BuildAsContainer() => new(Build<VisualElement>());
public void Clear()
public void Clear(bool all=false)
{
if (all)
{
visualElementProvider.GetVisualElement().Clear();
return;
}
foreach (var x in instances)
{
x.RemoveFromHierarchy();

View File

@ -0,0 +1,24 @@
{
"name": "BITKit.UX.Chart.Runtime",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:6de01b04fa4e14662b03fa46366da151",
"GUID:f19bbd83e3c264a5680926bf75d7e494"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [
"_SkiaSharp"
],
"versionDefines": [],
"noEngineReferences": false
}

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