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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit.StateMachine;
using UnityEngine;
namespace BITKit.Sensors.States
{
public abstract class SmartTargetSensorStates : ISmartTargetState
{
public bool Enabled { get; set; }
public virtual void Initialize()
{
}
public virtual void OnStateEntry(IState old)
{
}
public virtual void OnStateUpdate(float deltaTime)
{
}
public virtual void OnStateExit(IState old, IState newState)
{
}
}
[Serializable]
public sealed class Idle : SmartTargetSensorStates
{
}
}

View File

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