This commit is contained in:
parent
b673a9438d
commit
0362b2c606
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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": [],
|
||||||
|
|
|
@ -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="切削笔刷 用于各种切割" 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="切削笔刷 用于各种切割" 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" />
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace BITKit
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -19,5 +19,6 @@ namespace BITKit
|
||||||
}
|
}
|
||||||
return cacheName;
|
return cacheName;
|
||||||
}
|
}
|
||||||
|
public static int Hash => typeof(T).GetHashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "BITKit.Probability",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ namespace BITKit
|
||||||
{
|
{
|
||||||
public interface ITag
|
public interface ITag
|
||||||
{
|
{
|
||||||
|
int Hash { get; }
|
||||||
string[] GetTags();
|
string[] GetTags();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -12,4 +12,5 @@
|
||||||
T Get();
|
T Get();
|
||||||
void Set(T t);
|
void Set(T t);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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": [],
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>();
|
||||||
|
|
|
@ -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": [],
|
||||||
|
|
|
@ -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": [],
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "I18N",
|
"name": "Plugins.I18N",
|
||||||
"rootNamespace": "",
|
"rootNamespace": "",
|
||||||
"references": [],
|
"references": [],
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,7 +16,9 @@
|
||||||
"nunit.framework.dll"
|
"nunit.framework.dll"
|
||||||
],
|
],
|
||||||
"autoReferenced": true,
|
"autoReferenced": true,
|
||||||
"defineConstraints": [],
|
"defineConstraints": [
|
||||||
|
"UNITY_EDITOR"
|
||||||
|
],
|
||||||
"versionDefines": [],
|
"versionDefines": [],
|
||||||
"noEngineReferences": false
|
"noEngineReferences": false
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"name": "BITKit.Quadtree"
|
||||||
|
}
|
|
@ -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>>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Quadtree.Items
|
||||||
|
{
|
||||||
|
public abstract class GameObjectItem : GameObjectItemBase<GameObjectItem, Node<GameObjectItem>>
|
||||||
|
{
|
||||||
|
protected override GameObjectItem This() => this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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": [],
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue