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_PrefilterSSAOSampleCountHigh: 1
m_PrefilterDBufferMRT1: 1 m_PrefilterDBufferMRT1: 1
m_PrefilterDBufferMRT2: 1 m_PrefilterDBufferMRT2: 1
m_PrefilterDBufferMRT3: 1 m_PrefilterDBufferMRT3: 0
m_PrefilterSoftShadowsQualityLow: 1 m_PrefilterSoftShadowsQualityLow: 1
m_PrefilterSoftShadowsQualityMedium: 1 m_PrefilterSoftShadowsQualityMedium: 1
m_PrefilterSoftShadowsQualityHigh: 1 m_PrefilterSoftShadowsQualityHigh: 1

View File

@ -9775,6 +9775,7 @@ Transform:
- {fileID: 324953849} - {fileID: 324953849}
- {fileID: 495300441} - {fileID: 495300441}
- {fileID: 2087774128} - {fileID: 2087774128}
- {fileID: 554909162}
- {fileID: 1237214210} - {fileID: 1237214210}
- {fileID: 1228132877} - {fileID: 1228132877}
- {fileID: 676926185} - {fileID: 676926185}
@ -11200,6 +11201,69 @@ Transform:
m_CorrespondingSourceObject: {fileID: 1093686136660250980, guid: d25e6ff943432834c877c29af5e8ff40, type: 3} m_CorrespondingSourceObject: {fileID: 1093686136660250980, guid: d25e6ff943432834c877c29af5e8ff40, type: 3}
m_PrefabInstance: {fileID: 541966726} m_PrefabInstance: {fileID: 541966726}
m_PrefabAsset: {fileID: 0} 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 --- !u!1001 &555911125
PrefabInstance: PrefabInstance:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -12219,6 +12283,109 @@ Transform:
m_CorrespondingSourceObject: {fileID: 2723349985642794010, guid: d22a44843d244fc4fb4459be68ead0bb, type: 3} m_CorrespondingSourceObject: {fileID: 2723349985642794010, guid: d22a44843d244fc4fb4459be68ead0bb, type: 3}
m_PrefabInstance: {fileID: 596252691} m_PrefabInstance: {fileID: 596252691}
m_PrefabAsset: {fileID: 0} 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 --- !u!1001 &599941887
PrefabInstance: PrefabInstance:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -24689,6 +24856,7 @@ Transform:
- {fileID: 128062587} - {fileID: 128062587}
- {fileID: 857627674} - {fileID: 857627674}
- {fileID: 698951664} - {fileID: 698951664}
- {fileID: 599164995}
- {fileID: 1460596583} - {fileID: 1460596583}
m_Father: {fileID: 475671028} m_Father: {fileID: 475671028}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using AYellowpaper.SerializedCollections; using AYellowpaper.SerializedCollections;
@ -120,85 +121,7 @@ namespace BITFactory.Cutting
_brushFieldsContainer.Clear(); _brushFieldsContainer.Clear();
var fields = arg2.GetType().GetFields(ReflectionHelper.Flags); _brushFieldsContainer.Add(UXDataBindingService.Create(arg2));
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;
}
}
}
}
} }
} }
} }

View File

@ -1,10 +1,13 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using BITKit; using BITKit;
using BITKit.StateMachine; using BITKit.StateMachine;
using Newtonsoft.Json;
using PaintIn3D; using PaintIn3D;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe; using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics; using Unity.Mathematics;
using UnityEngine; using UnityEngine;
@ -26,14 +29,9 @@ namespace BITFactory.Cutting
public override string Name => "点切削"; public override string Name => "点切削";
public override string Description => "按住鼠标左键进行画笔切削"; public override string Description => "按住鼠标左键进行画笔切削";
[BITKit.Export("切削半径")] [Export("切削半径")]
public float Radius = 0; 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) public override void HandlePoint(bool isPreview, float3 normal, float3 point)
{ {
if (!isPreview) return; if (!isPreview) return;
@ -84,15 +82,6 @@ namespace BITFactory.Cutting
private float3 _normal=Vector3.up; private float3 _normal=Vector3.up;
private readonly IntervalUpdate _interrupt = new(0.1f); 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) public override void OnStateExit(IState old, IState newState)
{ {
base.OnStateExit(old, newState); base.OnStateExit(old, newState);
@ -183,7 +172,21 @@ namespace BITFactory.Cutting
{ {
[SerializeField] private Transform transform; [SerializeField] private Transform transform;
public override string Name => "填充切削"; 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:"自动填充")] [Export(name:"自动填充")]
private void AutoFill() private void AutoFill()

View File

@ -19,16 +19,6 @@ namespace BITFactory.Cutting
[Export("位置")] private Vector3 point; [Export("位置")] private Vector3 point;
[Export("半径")] private float radius = 0.01f; [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("确认")] [Export("确认")]
private void Release() private void Release()

View File

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

View File

@ -37,22 +37,31 @@
</ui:VisualElement> </ui:VisualElement>
<ui:Label tabindex="-1" text="笔刷" parse-escape-sequences="true" display-tooltip-when-elided="true" class="tl" style="display: none;" /> <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 picking-mode="Ignore" style="flex-direction: row;">
<ui:VisualElement name="cuttingBrush-container"> <ui:ScrollView>
<ui:Instance template="CuttingBrush" name="CuttingBrush" /> <ui:VisualElement name="cuttingBrush-container">
<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" 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:Instance template="CuttingBrush" name="CuttingBrush" /> <ui:Instance template="CuttingBrush" name="CuttingBrush" />
</ui:VisualElement> <ui:Instance template="CuttingBrush" name="CuttingBrush" />
<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:VisualElement> </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> </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:Button text="创意工坊" parse-escape-sequences="true" display-tooltip-when-elided="true" name="workshop-button" />
<ui:Foldout text="应用设置" class="--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="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:Button text="退出" parse-escape-sequences="true" display-tooltip-when-elided="true" name="exit-button" />
</ui:Foldout> </ui:Foldout>
<ui:VisualElement style="flex-direction: row; align-items: center;"> <ui:VisualElement style="flex-direction: row; align-items: center;">
@ -104,7 +114,7 @@
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement> <ui:VisualElement>
<ui:Label tabindex="-1" text="切削命令历史" parse-escape-sequences="true" display-tooltip-when-elided="true" name="Label" class="tl" /> <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: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" />
<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)] [AttributeUsage(AttributeTargets.Field)]
public class InjectAttribute : System.Attribute 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 readonly bool CanBeNull;
public InjectAttribute() public InjectAttribute()
{ {
@ -39,8 +50,7 @@ namespace BITKit
AsGlobal = asGlobal; AsGlobal = asGlobal;
} }
} }
#if Godot #if UNITY_64
#else
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ExportAttribute : System.Attribute public class ExportAttribute : System.Attribute
{ {

View File

@ -97,6 +97,22 @@ namespace BITKit
throw new NullReferenceException($"没有找到{typeof(T).Name}"); 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>() public static async Task<Interface> GetAsync<Interface>()
{ {
await TaskHelper.WaitUntil(() => dictionary.ContainsKey(typeof(Interface)) && dictionary[typeof(Interface) ]is not null); 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; 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)) if (index is not -1 && list.TryGetElementAt(index, out var nextElement))
{ {
nextElement.IsEntered = true; nextElement.IsEntered = true;
OnEntry?.Invoke(nextElement);
nextElement.Entry(); nextElement.Entry();
try try
{ {
@ -114,7 +115,7 @@ namespace BITKit
nextElement.Entered(); nextElement.Entered();
} }
catch (OperationCanceledException){} catch (OperationCanceledException){}
OnEntry?.Invoke(nextElement);
} }
} }
completed = true; completed = true;

View File

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

View File

@ -75,6 +75,21 @@ namespace BITKit
/// 已重构Items的回调 /// 已重构Items的回调
/// </summary> /// </summary>
event Action<IBasicItemContainer> OnRebuild; 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 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) public static bool Equals<T>(params T[] objs)
{ {
var a = objs.FirstOrDefault(); var a = objs.FirstOrDefault();
@ -60,7 +75,8 @@ namespace BITKit
{ {
foreach (var x in b) 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;
using System.Threading.Tasks; using System.Threading.Tasks;
using BITKit.IO; using BITKit.IO;
using BITKit.UX;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using Microsoft.CSharp; using Microsoft.CSharp;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -129,6 +130,10 @@ namespace BITKit.Mod
{ {
public static async UniTask<ModPackage[]> SearchPackages() 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 list=new List<ModPackage>();
var path = Path.Combine(Environment.CurrentDirectory, "Mods"); var path = Path.Combine(Environment.CurrentDirectory, "Mods");
var dir = new DirectoryInfo(path); var dir = new DirectoryInfo(path);
@ -142,10 +147,13 @@ namespace BITKit.Mod
package.WorkDirectory = x.FullName; package.WorkDirectory = x.FullName;
list.Add(package); list.Add(package);
} }
waiting?.Release(handle);
return list.ToArray(); return list.ToArray();
} }
public static async UniTask Reload() public static async UniTask Reload()
{ {
OnReload?.Invoke();
var mods = Mods; var mods = Mods;
foreach (var x in Mods) foreach (var x in Mods)
{ {
@ -170,6 +178,7 @@ namespace BITKit.Mod
BIT4Log.LogException(e); BIT4Log.LogException(e);
} }
} }
OnReloaded?.Invoke();
} }
public static IMod[] Mods { get; private set; }=Array.Empty<IMod>(); public static IMod[] Mods { get; private set; }=Array.Empty<IMod>();
@ -194,6 +203,9 @@ namespace BITKit.Mod
public static event Action<IMod> OnModUnLoaded; public static event Action<IMod> OnModUnLoaded;
public static event Action<IMod> OnModInstalled; public static event Action<IMod> OnModInstalled;
public static event Action<IMod> OnModUnInstalled; 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 Action<bool> OnLocked;
@ -266,37 +278,20 @@ namespace BITKit.Mod
while (_IsRunning) while (_IsRunning)
{ {
DI.TryGet<IUXWaiting>(out var waiting);
_CacheMods.Clear(); _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);
}
_CacheMods.Clear();
while (_UnRegisterQueue.TryDequeue(out var mod)) while (_UnRegisterQueue.TryDequeue(out var mod))
{ {
var handle = waiting?.Get();
handle?.SetMessage($":正在卸载{mod.PackageName}");
mod.OnDispose(); mod.OnDispose();
_CacheMods.Add(mod); _CacheMods.Add(mod);
OnModUnLoad?.Invoke(mod); OnModUnLoad?.Invoke(mod);
waiting?.Release(handle);
} }
foreach (var mod in _CacheMods) foreach (var mod in _CacheMods)
@ -314,6 +309,51 @@ namespace BITKit.Mod
OnModUnLoaded?.Invoke(mod); OnModUnLoaded?.Invoke(mod);
BIT4Log.Log<ModService>($"卸载Mod:{mod.GetType().FullName}"); 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); //Thread.Sleep(1000);
#if UNITY_64 #if UNITY_64
await UniTask.Delay(1000); await UniTask.Delay(1000);

View File

@ -1,3 +1,4 @@
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -36,8 +37,8 @@ namespace BITKit.Modification
/// </summary> /// </summary>
public interface IModifyElement public interface IModifyElement
{ {
IModifyElement[] Require { get; } IModifyElement[] Require => Array.Empty<IModifyElement>();
IModifyElement[] Incompatible { get; } IModifyElement[] Incompatible => Array.Empty<IModifyElement>();
} }
/// <summary> /// <summary>
/// 改装管理器 /// 改装管理器
@ -65,17 +66,32 @@ namespace BITKit.Modification
} }
public class ModifyManager : IModifyManager public class ModifyManager : IModifyManager
{ {
public virtual IDictionary<IModifyElement, object> Modifies { get;private set; } =new Dictionary<IModifyElement,object>(); public virtual IDictionary<IModifyElement, object> Modifies { get; } =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> Modified { get; } = new Dictionary<IModifyElement, object>();
public virtual void Add(IModifyElement modify, object obj) public virtual void Add(IModifyElement modify, object obj)
{ {
if(Modified.ContainsKey(modify))
throw new NotSupportModifyException(new[]{modify});
var list = new List<IModifyElement>(); var list = new List<IModifyElement>();
list.AddRange(Modified.Keys); list.AddRange(Modified.Keys);
list.Add(modify); 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(); var incompatible = list.Where(x=>x.Incompatible is not null).SelectMany(x => x.Incompatible).ToArray();
if(MathE.Contains(incompatible,list)) 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) 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>() public T GetProperty<T>()
{ {
return (T)properties[typeof(T).FullName]; return properties.OfType<T>().First();
} }
public void SetProperty<T>(T value) 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>() public bool Contains<T>()
{ {
return properties.ContainsKey(typeof(T).FullName); return properties.OfType<T>().FirstOrDefault() is not null;
} }
public T GetOrCreateProperty<T>() public T GetOrCreateProperty<T>()
{ {
return GetOrAddProperty(Activator.CreateInstance<T>); return GetOrAddProperty(Activator.CreateInstance<T>);
} }
public T GetOrAddProperty<T>(Func<T> addFactory) public T GetOrAddProperty<T>(Func<T> addFactory)
{ {
if (properties.TryGetValue(typeof(T).FullName, out var x)) foreach (var obj in properties)
{ {
return (T)x; if (obj is T t) return t;
}
else
{
AddPropertyInternal(addFactory.Invoke());
//properties.Add(typeof(T).FullName, x = addFactory.Invoke());
return (T)x;
} }
T x;
properties.Add(x = addFactory.Invoke());
return x;
} }
public bool TryGetProperty<T>(out T value) 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; return true;
} }
else catch (InvalidOperationException)
{ {
value=default(T); value = default;
return false; return false;
} }
} }
public bool TryRemoveProperty<T>() 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); properties.Remove(value);
return true; 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) public bool TrySetProperty<T>(T value)
{ {
bool result = false; var current = properties.OfType<T>().FirstOrDefault();
foreach (var pair in properties.Where(x=>x.Value is T).ToArray()) if (current is not null)
{ {
properties[pair.Key] = value; properties.Remove(current);
result = true; return true;
} }
return result; properties.Add(value);
// if (properties.TryGetValue(typeof(T).FullName, out var x)) return false;
// {
// properties[typeof(T).FullName] = value;
// return true;
// }
// else
// {
// return false;
// }
} }
public object[] GetProperties()=>properties.Values.Distinct().ToArray();
public object[] GetProperties() => properties.ToArray();
public void Read(BinaryReader r) public void Read(BinaryReader r)
{ {
@ -169,23 +171,8 @@ namespace BITKit
public bool CopyPropertiesFrom(IPropertable propertable) public bool CopyPropertiesFrom(IPropertable propertable)
{ {
ClearProperties(); ClearProperties();
foreach (var x in propertable.GetProperties()) properties.AddRange( propertable.GetProperties());;
{
AddPropertyInternal(x);
}
return true; 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 #if UNITY_64
using System.Collections; using System.Collections;
using BITKit.UX;
using UnityEngine; using UnityEngine;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using static BITKit.Mod.DotNetSdkRoslynService; using static BITKit.Mod.DotNetSdkRoslynService;
@ -60,6 +61,11 @@ namespace BITKit
public static Assembly Compile(params string[] codes) public static Assembly Compile(params string[] codes)
{ {
var outputPath = PathHelper.GetTempFilePath(); var outputPath = PathHelper.GetTempFilePath();
DI.TryGet<IUXWaiting>(out var waiting);
var handle = waiting?.Get();
handle?.SetMessage("正在编译");
try try
{ {
if (Installed is false) if (Installed is false)
@ -126,6 +132,8 @@ namespace BITKit
StandardInputEncoding = System.Text.Encoding.GetEncoding("gb2312"), StandardInputEncoding = System.Text.Encoding.GetEncoding("gb2312"),
StandardOutputEncoding = System.Text.Encoding.GetEncoding("gb2312"), StandardOutputEncoding = System.Text.Encoding.GetEncoding("gb2312"),
}; };
var process = new Process() var process = new Process()
{ {
StartInfo = StartInfo, StartInfo = StartInfo,
@ -165,12 +173,14 @@ namespace BITKit
} }
} }
waiting?.Release(handle);
return Assembly.Load(bytes); return Assembly.Load(bytes);
} }
catch (Exception e) catch (Exception e)
{ {
waiting?.Release(handle);
throw new Exception($"编译失败:{e}"); throw new Exception($"编译失败:{e}");
} }

View File

@ -2,6 +2,7 @@ namespace BITKit
{ {
public interface ITag public interface ITag
{ {
int Hash { get; }
string[] GetTags(); 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); 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) public void GetDirect<T>(int key,out T value)
{ {
try try

View File

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

View File

@ -17,7 +17,8 @@ namespace BITKit
{ {
public static string[] Cast(this IEnumerable<IReference> self) 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", "id": "07dfe885-d709-4482-9686-57f671b1aed6",
"expectedControlType": "Button", "expectedControlType": "Button",
"processors": "", "processors": "",
"interactions": "", "interactions": "Press",
"initialStateCheck": false "initialStateCheck": false
}, },
{ {

View File

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

View File

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

View File

@ -1,10 +1,10 @@
{ {
"name": "BITKit.WorkdChunk", "name": "BITKit.Pool",
"rootNamespace": "", "rootNamespace": "",
"references": [ "references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4", "GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:a9eec99827e569e45bfe3e5ea7494591", "GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:d8b63aba1907145bea998dd612889d6b" "GUID:49b49c76ee64f6b41bf28ef951cb0e50"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "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() private void OnAnimatorMove()
{ {
if (root) if (root)
root.SendMessage(nameof(OnAnimatorMove)); root.SendMessageUpwards(nameof(OnAnimatorMove),SendMessageOptions.DontRequireReceiver);
} }
private void AIAnimationEvent(string actionName) private void AIAnimationEvent(string actionName)
{ {
if (root) 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 Sensitivity = 1.81f;
public float TouchSensitivity = 0.22f; public float TouchSensitivity = 0.22f;
public float M_Yaw = 0.022f; 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 IDamageType { }
public interface IDamageService public interface IDamageService
{ {
public event Action<DamageMessage> OnEntityDamaged; public event Action<DamageMessage> OnEntityDamaged;
public event Action<DamageMessage> OnEntityKilled; public event Action<DamageMessage> OnEntityKilled;
void Execute(DamageMessage damageMessage); void Execute(DamageMessage damageMessage);
} }
public struct MeleeDamageMessage : IDamageType public struct MeleeDamageMessage : IDamageType
{ {
public bool Bleeding; public bool Bleeding;
@ -112,6 +110,7 @@ namespace BITKit.Entities
} }
public interface IDamagable public interface IDamagable
{ {
string Tag { get; }
IHealth Health { get; } IHealth Health { get; }
IUnityEntity UnityEntity { get; } IUnityEntity UnityEntity { get; }
Rigidbody Rigidbody { get; } Rigidbody Rigidbody { get; }
@ -119,7 +118,7 @@ namespace BITKit.Entities
} }
public class DamageService:MonoBehaviour,IDamageService public class DamageService:MonoBehaviour,IDamageService
{ {
internal static IDamageService Singleton { get; set; } public static IDamageService Singleton { get;private set; }
private readonly Queue<DamageMessage> Messages = new(); private readonly Queue<DamageMessage> Messages = new();
[SerializeReference,SubclassSelector] private INetClient netClient; [SerializeReference,SubclassSelector] private INetClient netClient;

View File

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

View File

@ -4,19 +4,55 @@ using UnityEngine;
using BITKit; using BITKit;
namespace BITKit.Entities 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) public void GiveDamage(DamageMessage message)
{ {
UnityEntity.Invoke(message); if (_unityEntity is not null)
UnityEntity.Invoke(message);
} }
[SerializeField]private Rigidbody m_rigidbody; [SerializeField]private Rigidbody m_rigidbody;
[SerializeReference, SubclassSelector] private new IReference tag;
[Inject] [Inject(true)]
private IHealth _health; 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 foreach (var fieldInfo in obj
.GetType() .GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .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 type = fieldInfo.FieldType;
var attribute = fieldInfo.GetCustomAttribute<InjectAttribute>(); var attribute = fieldInfo.GetCustomAttribute<InjectAttribute>();

View File

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

View File

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

View File

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

View File

@ -16,11 +16,14 @@ namespace BITKit
[SerializeField] private Vector3 positionWeight = Vector3.one; [SerializeField] private Vector3 positionWeight = Vector3.one;
[SerializeReference,SubclassSelector] private IReference autoReference; [SerializeReference,SubclassSelector] private IReference autoReference;
[SerializeReference,SubclassSelector] private IReference nameReference;
//[SerializeField] private Vector3 rotationAdditive = Vector3.one; //[SerializeField] private Vector3 rotationAdditive = Vector3.one;
private Transform Transform; private Transform Transform;
private string nameCache;
private void Start() private void Start()
{ {
nameCache = nameReference is not null?nameReference.Value:gameObject.name;
Transform = transform; Transform = transform;
var components = var components =
GetComponentsInParent<LocationAdditive>(true) GetComponentsInParent<LocationAdditive>(true)
@ -39,7 +42,6 @@ namespace BITKit
reportBuilder.AppendLine(x.autoReference is not null ? $"{x.autoReference.Value}@{x.transform.name}" : x.name); reportBuilder.AppendLine(x.autoReference is not null ? $"{x.autoReference.Value}@{x.transform.name}" : x.name);
} }
BIT4Log.Log<LocationAdditiveElement>($"找不到对应的LocationAdditive:{autoReference.Value} \n{reportBuilder}"); BIT4Log.Log<LocationAdditiveElement>($"找不到对应的LocationAdditive:{autoReference.Value} \n{reportBuilder}");
throw;
} }
} }
@ -58,11 +60,12 @@ namespace BITKit
private void Tick() private void Tick()
{ {
if (!locationAdditive) return;
var localPosition = Transform.localPosition; 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 * 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.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using BITKit.UX;
#if UNITY_EDITOR #if UNITY_EDITOR
using UnityEditor; using UnityEditor;
@ -18,7 +19,10 @@ namespace BITKit.Mod
[SerializeField] private bool loadLocalPackageOnStart; [SerializeField] private bool loadLocalPackageOnStart;
private async void Start() private async void Start()
{ {
DI.TryGet<IUXWaiting>(out var waiting);
var handle = waiting?.Get();
handle?.SetMessage("正在初始化Mod服务");
if (Application.isEditor is false) if (Application.isEditor is false)
{ {
BIT4Log.Log<UnityModService>($"UnityPlayer所在位置:{Application.dataPath}"); BIT4Log.Log<UnityModService>($"UnityPlayer所在位置:{Application.dataPath}");
@ -71,6 +75,8 @@ namespace BITKit.Mod
{ {
ModService.OnPackageLoad-=OnPackageLoad; ModService.OnPackageLoad-=OnPackageLoad;
}); });
waiting?.Release(handle);
} }
private void OnPackageLoad(ModPackage obj) private void OnPackageLoad(ModPackage obj)
@ -106,8 +112,9 @@ namespace BITKit.Mod
{ {
dll = Path.Combine(folder,"Data", "MonoBleedingEdge", "lib","mono","unityjit-win32","Facades",dllName); dll = Path.Combine(folder,"Data", "MonoBleedingEdge", "lib","mono","unityjit-win32","Facades",dllName);
} }
#else #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 #endif
return File.Exists(dll); 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" "nunit.framework.dll"
], ],
"autoReferenced": true, "autoReferenced": true,
"defineConstraints": [], "defineConstraints": [
"UNITY_EDITOR"
],
"versionDefines": [], "versionDefines": [],
"noEngineReferences": false "noEngineReferences": false
} }

View File

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

View File

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

View File

@ -1,10 +1,9 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using BITKit.Events; using BITKit.Events;
using UnityEditor;
using UnityEngine; using UnityEngine;
namespace BITKit.Physics namespace BITKit
{ {
public class Prop_Physics : MonoBehaviour 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> /// </summary>
event Action<string> OnSceneLoaded; event Action<string> OnSceneLoaded;
/// <summary>
/// 注册加载任务
/// </summary>
void RegisterLoadTaskAsync(Func<UniTask> task);
/// <summary>
/// 注销加载任务
/// </summary>
/// <param name="task"></param>
void UnRegisterLoadTaskAsync(Func<UniTask> task);
/// <summary> /// <summary>
/// 当开始卸载场景时 /// 当开始卸载场景时
/// </summary> /// </summary>
@ -89,6 +98,16 @@ namespace BITKit.SceneManagement
remove => _sceneServiceImplementation.OnSceneLoaded -= value; 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 public event Action<string> OnUnloadScene
{ {
add => _sceneServiceImplementation1.OnUnloadScene += value; add => _sceneServiceImplementation1.OnUnloadScene += value;

View File

@ -1,10 +1,12 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using UnityEditor; using UnityEditor;
using UnityEditor.UIElements; using UnityEditor.UIElements;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements; using UnityEngine.UIElements;
using Object = UnityEngine.Object;
namespace BITKit namespace BITKit
{ {
@ -19,6 +21,8 @@ namespace BITKit
private Button buildButton; private Button buildButton;
private Label _titleLabel; private Label _titleLabel;
private VisualElement _container; private VisualElement _container;
private MeshRenderer[] _renderers=Array.Empty<MeshRenderer>();
private void OnEnable() private void OnEnable()
{ {
@ -38,22 +42,52 @@ namespace BITKit
private void OnSelectionChanged() private void OnSelectionChanged()
{ {
var active = UnityEditor.Selection.activeGameObject; var actives = UnityEditor.Selection.gameObjects;
_container.Clear(); _container.Clear();
_titleLabel.text = active ? active.name : "未选择物体"; _renderers = actives.SelectMany(x => x.GetComponentsInChildren<MeshRenderer>()).ToArray();
if (!active) return;
foreach (var x in active.GetComponentsInChildren<MeshRenderer>(true)) 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>(); var current = sharedMaterials[i];
filed.label = material.name; if(current != evt.previousValue) continue;
filed.objectType = typeof(Material); sharedMaterials[i] = value;
filed.value = material; 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; 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 public event Action<string> OnUnloadScene
{ {
add => _sceneServiceImplementation.OnUnloadScene += value; add => _sceneServiceImplementation.OnUnloadScene += value;
@ -198,9 +208,15 @@ namespace BITKit.SceneManagement
OnSceneLoadProgress?.Invoke(sceneName, progress); OnSceneLoadProgress?.Invoke(sceneName, progress);
} }
LoadedObjects.Add(sceneName, handle.SceneObject); LoadedObjects.Add(sceneName, handle.SceneObject);
OnSceneLoadProgress?.Invoke(sceneName, 1); OnSceneLoadProgress?.Invoke(sceneName, 1);
await Task.Delay(384, cancellationToken); await Task.Delay(384, cancellationToken);
foreach (var x in _onSceneLoadedAsyncList.ToArray())
{
await x.Invoke();
if (destroyCancellationToken.IsCancellationRequested) return;
}
OnSceneLoaded?.Invoke(sceneName); OnSceneLoaded?.Invoke(sceneName);
stopwatchWatcher.Stop(); stopwatchWatcher.Stop();
// if (activateOnLoad is false) // if (activateOnLoad is false)
@ -252,6 +268,16 @@ namespace BITKit.SceneManagement
remove => OnSceneLoaded -= value; 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> OnUnloadScene;
public event Action<string> OnSceneUnloaded; public event Action<string> OnSceneUnloaded;
} }

View File

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

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:f51ebe6a0ceec4240a699833d6309b23",
"GUID:be17a8778dbfe454890ed8279279e153", "GUID:be17a8778dbfe454890ed8279279e153",
"GUID:14fe60d984bf9f84eac55c6ea033a8f4", "GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:9400d40641bab5b4a9702f65bf5c6eb5" "GUID:9400d40641bab5b4a9702f65bf5c6eb5",
"GUID:1193c2664d97cc049a6e4c486c6bce71"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],

View File

@ -17,6 +17,7 @@ namespace BITKit.Sensors
/// </summary> /// </summary>
public interface ISensor public interface ISensor
{ {
int Id { get; }
/// <summary> /// <summary>
/// 自动更新 /// 自动更新
/// </summary> /// </summary>
@ -45,7 +46,7 @@ namespace BITKit.Sensors
/// 传感器执行检测 /// 传感器执行检测
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
UniTask Execute(); UniTask Execute(float delta);
} }
[Serializable] [Serializable]
@ -53,6 +54,8 @@ namespace BITKit.Sensors
{ {
[SerializeField] private GameObject gameObject; [SerializeField] private GameObject gameObject;
private ISensor _sensorImplementation => gameObject.GetComponent<ISensor>(); private ISensor _sensorImplementation => gameObject.GetComponent<ISensor>();
public int Id => _sensorImplementation.Id;
public IEnumerable<Transform> Get() public IEnumerable<Transform> Get()
{ {
return _sensorImplementation.Get(); return _sensorImplementation.Get();
@ -67,10 +70,9 @@ namespace BITKit.Sensors
{ {
return _sensorImplementation.GetDistance(); return _sensorImplementation.GetDistance();
} }
public UniTask Execute(float delta)
public UniTask Execute()
{ {
return _sensorImplementation.Execute(); return _sensorImplementation.Execute(delta);
} }
} }
[System.Serializable] [System.Serializable]
@ -78,6 +80,8 @@ namespace BITKit.Sensors
{ {
[SerializeField] private MonoBehaviour monoBehaviour; [SerializeField] private MonoBehaviour monoBehaviour;
private ISensor _sensorImplementation=>monoBehaviour as ISensor; private ISensor _sensorImplementation=>monoBehaviour as ISensor;
public int Id => _sensorImplementation.Id;
public IEnumerable<Transform> Get() public IEnumerable<Transform> Get()
{ {
return _sensorImplementation.Get(); return _sensorImplementation.Get();
@ -93,9 +97,9 @@ namespace BITKit.Sensors
return _sensorImplementation.GetDistance(); return _sensorImplementation.GetDistance();
} }
public UniTask Execute() public UniTask Execute(float delta)
{ {
return _sensorImplementation.Execute(); return _sensorImplementation.Execute(delta);
} }
} }
public abstract class Sensor : MonoBehaviour, ISensor public abstract class Sensor : MonoBehaviour, ISensor
@ -106,16 +110,15 @@ namespace BITKit.Sensors
[Header(Constant.Header.Gameobjects)] [Header(Constant.Header.Gameobjects)]
public Collider[] ignoreColliders; public Collider[] ignoreColliders;
[Header(Constant.Header.InternalVariables)] [Header(Constant.Header.InternalVariables)]
[NonSerialized] [SerializeField,ReadOnly]
public Transform[] detected = Array.Empty<Transform>(); public Transform[] detected = Array.Empty<Transform>();
public abstract IEnumerable<Transform> Get(); public abstract IEnumerable<Transform> Get();
public abstract bool IsValid(Collider _collider); public abstract bool IsValid(Collider _collider);
public abstract UniTask Execute();
public abstract float GetDistance(); public abstract float GetDistance();
public virtual UniTask Execute(float delta)=>UniTask.CompletedTask;
public int Id { get; private set; }
bool ISensor.AutoUpdate => autoUpdate; bool ISensor.AutoUpdate => autoUpdate;
protected int Id;
protected Transform Transform; protected Transform Transform;
protected virtual void OnEnable() 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 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 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 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.Jobs;
using UnityEngine.Pool; using UnityEngine.Pool;
using UnityEngine.Profiling; using UnityEngine.Profiling;
using Physics=UnityEngine.Physics;
namespace BITKit.Sensors namespace BITKit.Sensors
{ {
public class RangeSensor : Sensor public class RangeSensor : Sensor
{ {
[Header(Constant.Header.Settings)] [Header(Constant.Header.Settings)] public float radius;
public float radius;
public int fov; public int fov;
public bool requireSight; 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;
private bool isExecuting; [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();
public override IEnumerable<Transform> Get() public override IEnumerable<Transform> Get()
{ {
if (!_detectedDoubleBuffer.TryGetRelease(out var newRelease)) return _detectedBuffer; if (!_detectedDoubleBuffer.TryGetRelease(out var newRelease)) return _detectedBuffer;
@ -33,12 +34,13 @@ namespace BITKit.Sensors
Profiler.EndSample(); Profiler.EndSample();
return _detectedBuffer; return _detectedBuffer;
} }
private readonly DoubleBuffer<IEnumerable<Transform>> _detectedDoubleBuffer=new();
private IEnumerable<Transform> _detectedBuffer;
private readonly DoubleBuffer<IEnumerable<Transform>> _detectedDoubleBuffer = new();
public override UniTask Execute() private IEnumerable<Transform> _detectedBuffer=Array.Empty<Transform>();
public override UniTask Execute(float delta)
{ {
tempHashSet.Clear();
var length = Physics.OverlapSphereNonAlloc(Transform.position, radius, colliders, detectLayer); var length = Physics.OverlapSphereNonAlloc(Transform.position, radius, colliders, detectLayer);
Profiler.BeginSample("Filter Detected Colliders"); Profiler.BeginSample("Filter Detected Colliders");
var _newDetected = from x in colliders.Take(length) where IsValid(x) select x.transform; 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): case var _ when ignoreColliders.Contains(_collider):
return false; return false;
case var _ when fov > 0 && CheckFov(ref _collider) is 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; return false;
default: default:
return true; return true;
@ -62,18 +64,21 @@ namespace BITKit.Sensors
} }
public override float GetDistance() => radius; public override float GetDistance() => radius;
private bool CheckFov(ref Collider _collider) private bool CheckFov(ref Collider _collider)
{ {
var _dir = _collider.transform.position - transform.position; var _dir = _collider.bounds.center - transform.position;
if (_dir.sqrMagnitude <= 0) return false; if (_dir.sqrMagnitude <= 0) return false;
var dir = Quaternion.LookRotation(_dir); var dir = Quaternion.LookRotation(_dir);
return Vector3.Dot(transform.forward, _dir) > 0 && fov > Quaternion.Angle(transform.rotation, dir); return Vector3.Dot(transform.forward, _dir) > 0 && fov > Quaternion.Angle(transform.rotation, dir);
} }
private bool CheckSight(ref Collider _collider) private bool CheckSight(ref Collider _collider)
{ {
if (!requireSight) return false; if (!requireSight) return false;
var transform1 = _collider.transform; var position = _collider.bounds.center;
var position = transform1.position; //是检测bounds的顶部
position.y += _collider.bounds.extents.y;
var location = new Location(Transform); var location = new Location(Transform);
var length = Physics.RaycastNonAlloc( var length = Physics.RaycastNonAlloc(
location.position, location.position,
@ -82,23 +87,32 @@ namespace BITKit.Sensors
Vector3.Distance(location, position), Vector3.Distance(location, position),
blockLayer blockLayer
); );
switch (length) switch (length)
{ {
case 0: case 0:
Debug.DrawLine(location, position, Color.green, 1);
return true; return true;
case 1: case 1:
if (hits[0].collider == _collider) return hits[0].collider == _collider;
{ default:
return true; var collider1 = _collider;
} if (hits.Take(length).Any(Predicate))
break;
default:
if (hits.Take(length).Any(x => ignoreColliders.Contains(x.collider) is false))
{ {
return false; return false;
} }
break; 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; return true;
} }
} }

View File

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

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
@ -10,6 +11,7 @@ namespace BITKit.Sensors
public class SensorQueue : MonoBehaviour public class SensorQueue : MonoBehaviour
{ {
internal static readonly Dictionary<int,ISensor> Sensors=new(); internal static readonly Dictionary<int,ISensor> Sensors=new();
internal static readonly ConcurrentDictionary<int, float> LastDetectedTime = new();
private static bool IsDirty; private static bool IsDirty;
[SerializeField,ReadOnly] private int _position; [SerializeField,ReadOnly] private int _position;
@ -32,10 +34,25 @@ namespace BITKit.Sensors
} }
[SerializeField] private MonoBehaviour[] 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; if (SensorGlobalSettings.Enabled is false) return;
_isBusy = true;
if(IsDirty) if(IsDirty)
{ {
_position = 0; _position = 0;
@ -44,11 +61,25 @@ namespace BITKit.Sensors
sensors = Sensors.Values.Where(IsEnabled).OfType<MonoBehaviour>().ToArray(); sensors = Sensors.Values.Where(IsEnabled).OfType<MonoBehaviour>().ToArray();
} }
if(Sensors.Count is 0) return; if (Sensors.Count is 0)
{
Sensors.ElementAt(_position++).Value.Execute().Forget(); _isBusy = false;
return;
}
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; _position %= Sensors.Count;
_isBusy = false;
} }
private bool IsEnabled(ISensor sensor) private bool IsEnabled(ISensor sensor)
{ {
@ -60,4 +91,4 @@ namespace BITKit.Sensors
} }
} }
} }

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using BITKit.Sensors.States;
using BITKit.StateMachine;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
#if UNITY_EDITOR #if UNITY_EDITOR
using UnityEditor; using UnityEditor;
@ -16,212 +18,38 @@ using UnityEngine.UIElements;
namespace BITKit.Sensors namespace BITKit.Sensors
{ {
public interface ISmartTargetProperty{}
public interface ISmartTargetState:IState{}
/// <summary> /// <summary>
/// 智能目标传感器,根据条件,智能选择目标 /// 智能目标传感器,根据条件,智能选择目标
/// </summary> /// </summary>
public class SmartTargetSensor :MonoBehaviour,ISensor,IAction public class SmartTargetSensor :StateBasedMonoBehaviour<ISmartTargetState>,ISensor
{ {
/// <summary> [SerializeField] private float radius;
/// 自动更新 [SerializeField] private RangeSensor rangeSensor;
/// </summary> [SerializeField] private AudioSensor audioSensor;
[Header(Constant.Header.Settings)] public int Id { get; set; }
[SerializeField] private bool autoUpdate; private readonly CacheList<Transform> _detected=new();
[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();
private void OnEnable() private void OnEnable()
{ {
SensorQueue.Register(Id=GetInstanceID(),this); Id = GetInstanceID();
SensorQueue.Register(Id,this);
}
private void Start()
{
TransitionState<Idle>();
} }
private void OnDisable() private void OnDisable()
{ {
SensorQueue.UnRegister(Id); 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 CurrentState?.OnStateUpdate(delta);
{ return UniTask.CompletedTask;
_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;
}
} }
} }
#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); onLost.Invoke(_collider);
} }
} }
public int Id => _id is 0 ? _id = GetInstanceID() : _id;
private int _id;
public IEnumerable<Transform> Get() public IEnumerable<Transform> Get()
{ {
@ -89,13 +91,11 @@ namespace BITKit.Sensors
if (ignores.Contains(detectedObject)) return false; if (ignores.Contains(detectedObject)) return false;
return !detectedLayer.Allow || detectedLayer.Value.Includes(_collider.gameObject.layer); return !detectedLayer.Allow || detectedLayer.Value.Includes(_collider.gameObject.layer);
} }
public float GetDistance() public float GetDistance()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public UniTask Execute(float delta = 0)=>
public UniTask Execute()=>
UniTask.CompletedTask; UniTask.CompletedTask;
private void Update() private void Update()

View File

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

View File

@ -13,8 +13,14 @@ namespace BITKit
[SerializeField] private string[] tags; [SerializeField] private string[] tags;
[Tooltip("Disable when tags is not empty")] [Tooltip("Disable when tags is not empty")]
[SerializeReference,SubclassSelector] private IReference[] reference; [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; public string[] GetTags() => CacheTags ??= reference?.Length > 0 ? reference.Select(x => x.Value).ToArray() : tags;
private string[] CacheTags; 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.Collections.Generic;
using System.Timers; using System.Timers;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine; using UnityEngine;
namespace BITKit namespace BITKit
@ -71,9 +74,16 @@ namespace BITKit
TickCount++; TickCount++;
try try
{ {
var delta = (float)(BITApp.Time.TimeAsDouble - _lastTime); var delta = (float)(BITApp.Time.TimeAsDouble - _lastTime);
_lastTime = BITApp.Time.TimeAsDouble; _lastTime = BITApp.Time.TimeAsDouble;
if (isMainThread) await UniTask.SwitchToMainThread(destroyCancellationToken); if (isMainThread) await UniTask.SwitchToMainThread(destroyCancellationToken);
#if UNITY_EDITOR
if (EditorApplication.isPlaying is false)
{
Restart();
}
#endif
while (_ActionQueue.TryDequeue(out var action)) while (_ActionQueue.TryDequeue(out var action))
{ {
action?.Invoke(); action?.Invoke();
@ -95,10 +105,16 @@ _TickEvents?.Invoke(delta);
BIT4Log.LogException(exception); 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 System.Timers;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using UnityEngine; using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace BITKit namespace BITKit
{ {
@ -48,11 +51,23 @@ namespace BITKit
{ {
public event Action<float> Tick; public event Action<float> Tick;
public float Interval { get; set; } public float Interval { get; set; }
public async void Invoke(object sender, ElapsedEventArgs args) public async void Invoke(object sender, ElapsedEventArgs args)
{ {
await UniTask.SwitchToMainThread(); 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) public static void Add(Action<float> action, float interval)
@ -81,7 +96,6 @@ namespace BITKit
{ {
Interval = totalMilliseconds, Interval = totalMilliseconds,
}; };
timer.AutoReset = true;
_CreateTimes.TryAdd(interval, GameTickService.TickCount); _CreateTimes.TryAdd(interval, GameTickService.TickCount);
var action = _Actions.GetOrAdd(interval, CreateAction); var action = _Actions.GetOrAdd(interval, CreateAction);
@ -91,6 +105,13 @@ namespace BITKit
BIT4Log.Log<IntervalTickService>($"已创建Tick,Interval[{totalMilliseconds}]"); BIT4Log.Log<IntervalTickService>($"已创建Tick,Interval[{totalMilliseconds}]");
return timer; return timer;
void Tick(object sender, ElapsedEventArgs args)
{
action.Invoke(sender, args);
if (timer.Enabled)
timer.Start();
}
} }
private static DelegateWrapper CreateAction(float interval)=>new() private static DelegateWrapper CreateAction(float interval)=>new()
{ {

View File

@ -101,7 +101,7 @@ namespace BITKit.UX
[UXBindPath(UXConstant.TitleLabel)] [UXBindPath(UXConstant.TitleLabel)]
private Label _titleLabel; private Label _titleLabel;
[UXBindPath(UXConstant.ContextLabel)] [UXBindPath(UXConstant.ContextLabel)]
private Label _contextLabel; private TextElement _contextLabel;
[UXBindPath(UXConstant.MainButton)] [UXBindPath(UXConstant.MainButton)]
private Button _comfirmButton; private Button _comfirmButton;
[UXBindPath(UXConstant.SecButton)] [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(Func<VisualElement> createFactory) => new(Build<VisualElement>(createFactory));
public UXContainer BuildAsContainer() => new(Build<VisualElement>()); 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) foreach (var x in instances)
{ {
x.RemoveFromHierarchy(); 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