1
This commit is contained in:
@@ -4,7 +4,7 @@ using System.Collections.Generic;
|
||||
using BITKit.Core.Tuple;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BITKit.Physics
|
||||
namespace BITKit
|
||||
{
|
||||
[Serializable]
|
||||
public class JointConfigure
|
||||
|
@@ -1,10 +1,9 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using BITKit.Events;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BITKit.Physics
|
||||
namespace BITKit
|
||||
{
|
||||
public class Prop_Physics : MonoBehaviour
|
||||
{
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
@@ -15,9 +16,18 @@ namespace BITKit.Sensors
|
||||
public class AudioSensor : MonoBehaviour,ISensor
|
||||
{
|
||||
[Header(Constant.Header.Settings)]
|
||||
[SerializeField] private bool autoUpdate;
|
||||
[SerializeField]private float radius;
|
||||
[SerializeField] private Optional<string[]> ignoreTags;
|
||||
private readonly CacheList<Transform> cache = new();
|
||||
private void OnEnable()
|
||||
{
|
||||
Id = GetInstanceID();
|
||||
SensorQueue.Register(Id,this);
|
||||
}
|
||||
private void OnDisable()
|
||||
{
|
||||
SensorQueue.UnRegister(Id);
|
||||
}
|
||||
public UniTask Execute(float delta)
|
||||
{
|
||||
var position = transform.position;
|
||||
@@ -26,7 +36,6 @@ namespace BITKit.Sensors
|
||||
{
|
||||
var distance = Vector3.Distance(position, x.Position);
|
||||
if(distance>radius) continue;
|
||||
if(ignoreTags.Allow && x.Tag is { } tag1 && tag1.GetTags().Any(ignoreTags.Value.Contains)) continue;
|
||||
cache.Add(x.Transform);
|
||||
}
|
||||
return UniTask.CompletedTask;
|
||||
@@ -35,5 +44,6 @@ namespace BITKit.Sensors
|
||||
public IEnumerable<Transform> Get() => cache.ValueArray;
|
||||
public bool IsValid(Collider _collider) => false;
|
||||
public float GetDistance() => radius;
|
||||
public bool AutoUpdate=>autoUpdate;
|
||||
}
|
||||
}
|
@@ -7,6 +7,7 @@ using Cysharp.Threading.Tasks;
|
||||
using UnityEngine.Jobs;
|
||||
using UnityEngine.Pool;
|
||||
using UnityEngine.Profiling;
|
||||
using Physics=UnityEngine.Physics;
|
||||
|
||||
namespace BITKit.Sensors
|
||||
{
|
||||
@@ -35,12 +36,12 @@ namespace BITKit.Sensors
|
||||
}
|
||||
|
||||
private readonly DoubleBuffer<IEnumerable<Transform>> _detectedDoubleBuffer = new();
|
||||
private IEnumerable<Transform> _detectedBuffer;
|
||||
private IEnumerable<Transform> _detectedBuffer=Array.Empty<Transform>();
|
||||
|
||||
public override UniTask Execute(float delta)
|
||||
{
|
||||
tempHashSet.Clear();
|
||||
var length = UnityEngine.Physics.OverlapSphereNonAlloc(Transform.position, radius, colliders, detectLayer);
|
||||
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;
|
||||
Profiler.EndSample();
|
||||
@@ -93,13 +94,7 @@ namespace BITKit.Sensors
|
||||
Debug.DrawLine(location, position, Color.green, 1);
|
||||
return true;
|
||||
case 1:
|
||||
if (hits[0].collider == _collider)
|
||||
{
|
||||
Debug.DrawLine(location, hits[0].point, Color.blue, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
return hits[0].collider == _collider;
|
||||
default:
|
||||
var collider1 = _collider;
|
||||
if (hits.Take(length).Any(Predicate))
|
||||
|
@@ -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,270 +18,38 @@ using UnityEngine.UIElements;
|
||||
|
||||
namespace BITKit.Sensors
|
||||
{
|
||||
public interface ISmartTargetProperty{}
|
||||
public interface ISmartTargetState:IState{}
|
||||
/// <summary>
|
||||
/// 智能目标传感器,根据条件,智能选择目标
|
||||
/// </summary>
|
||||
public class SmartTargetSensor :MonoBehaviour,ISensor
|
||||
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;
|
||||
[SerializeField] private Optional<int> lostTargetInterval;
|
||||
[SerializeField] private Optional<float> detectedTime;
|
||||
[SerializeField] private Transform root;
|
||||
/// <summary>
|
||||
/// 主传感器
|
||||
/// </summary>
|
||||
[Header(nameof(Sensor))]
|
||||
[SerializeField,SerializeReference,SubclassSelector] private ISensor sensor;
|
||||
[SerializeField] private float radius;
|
||||
[SerializeField] private RangeSensor rangeSensor;
|
||||
[SerializeField] private AudioSensor audioSensor;
|
||||
|
||||
[Header(Constant.Header.Debug)]
|
||||
[SerializeReference, ReadOnly] private int updateCount;
|
||||
|
||||
public IEnumerable<Transform> Get() =>CurrentTarget is not null ? new[] { CurrentTarget }:Enumerable.Empty<Transform>();
|
||||
public int Id { get;private set; }
|
||||
bool ISensor.AutoUpdate => autoUpdate;
|
||||
|
||||
internal StringBuilder reportBuilder;
|
||||
internal readonly DoubleBuffer<string> report=new();
|
||||
internal readonly IntervalUpdate lostTargetIntervalUpdate = 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; }
|
||||
public ISensorTarget SensorTarget { get; private set; }
|
||||
public float TargetWeight
|
||||
{
|
||||
get => _targetWeight;
|
||||
set => _targetWeight = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
private float _targetWeight;
|
||||
|
||||
private Vector3 _currentPosition;
|
||||
|
||||
private CancellationTokenSource timeoutCancellationTokenSource=new();
|
||||
|
||||
public int Id { get; set; }
|
||||
private readonly CacheList<Transform> _detected=new();
|
||||
private void OnEnable()
|
||||
{
|
||||
lostTargetIntervalUpdate.Interval = lostTargetInterval;
|
||||
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(float delta)
|
||||
public IEnumerable<Transform> Get() => _detected.ValueArray;
|
||||
public bool IsValid(Collider _collider) => false;
|
||||
public float GetDistance() => radius;
|
||||
public UniTask Execute(float delta)
|
||||
{
|
||||
delta=Mathf.Clamp(delta,0.01f,2);
|
||||
try
|
||||
{
|
||||
_currentPosition = transform.position;
|
||||
updateCount++;
|
||||
timeoutCancellationTokenSource?.Cancel();
|
||||
timeoutCancellationTokenSource = new CancellationTokenSource();
|
||||
await sensor.Execute(delta);
|
||||
|
||||
if (audioSensor)
|
||||
{
|
||||
await audioSensor.Execute(delta);
|
||||
}
|
||||
|
||||
reportBuilder?.AppendLine($"-----BEGIN------{updateCount}");
|
||||
|
||||
Profiler.BeginSample("Release Detected Buffer");
|
||||
|
||||
if (audioSensor)
|
||||
{
|
||||
var heard = audioSensor.Get();
|
||||
if(heard.Any())
|
||||
{
|
||||
CurrentTarget = heard.First();
|
||||
reportBuilder?.AppendLine($"Heard:{CurrentTarget.name}");
|
||||
reportBuilder?.AppendLine($"-----Complete------{updateCount}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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)||newDetected.Contains(SensorTarget?.Transform))
|
||||
{
|
||||
lostTargetIntervalUpdate.Reset();
|
||||
if (optionalRetargetInterval.Allow && optionalRetargetInterval.Value.AllowUpdate)
|
||||
{
|
||||
reportBuilder?.AppendLine("Retarget Interval Catch,Search New Target");
|
||||
}
|
||||
else
|
||||
{
|
||||
reportBuilder?.AppendLine("Current Target Detected,Keep Target");
|
||||
reportBuilder?.AppendLine($"-----Complete------{updateCount}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(lostTargetInterval.Allow && CurrentTarget is null)
|
||||
{
|
||||
if (lostTargetIntervalUpdate.AllowUpdateWithoutReset)
|
||||
{
|
||||
CurrentTarget = null;
|
||||
reportBuilder?.AppendLine("Lost Target,Search New Target");
|
||||
}
|
||||
else if(CurrentTarget is null )
|
||||
{
|
||||
SensorTarget?.Detected(TargetWeight-=delta, this, root?root:this);
|
||||
|
||||
if (TargetWeight <= 0)
|
||||
{
|
||||
SensorTarget = null;
|
||||
}
|
||||
reportBuilder?.AppendLine($"Lost Target,Forget After {lostTargetIntervalUpdate.Remaining}s");
|
||||
}
|
||||
}
|
||||
Profiler.BeginSample("Filter Detected");
|
||||
|
||||
foreach (var x in newDetected.OrderBy(KeySelector))
|
||||
{
|
||||
if(IsValid(x.GetComponent<Collider>()) is false)continue;
|
||||
SensorTarget = x.GetComponent<ISensorTarget>();
|
||||
if (detectedTime.Allow && CurrentTarget is null)
|
||||
{
|
||||
TargetWeight +=detectedTime.Value / delta;
|
||||
SensorTarget?.Detected(TargetWeight, this, root?root:this);
|
||||
reportBuilder?.AppendLine($"New Target See,Weight is:{TargetWeight}");
|
||||
}
|
||||
if (detectedTime.Allow is false || TargetWeight >= 1 && CurrentTarget != x)
|
||||
{
|
||||
CurrentTarget = x;
|
||||
SensorTarget?.Detected(1, this, root?root:this);
|
||||
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;
|
||||
}
|
||||
#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
|
||||
}
|
||||
|
@@ -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
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@@ -13,7 +13,8 @@ namespace BITKit
|
||||
[SerializeField] private string[] tags;
|
||||
[Tooltip("Disable when tags is not empty")]
|
||||
[SerializeReference,SubclassSelector] private IReference[] reference;
|
||||
|
||||
public int Hash => _id is 0 ? _id = MathE.GetHash(GetTags()) : _id;
|
||||
private int _id;
|
||||
public string[] GetTags() => CacheTags ??= reference?.Length > 0 ? reference.Select(x => x.Value).ToArray() : tags;
|
||||
private string[] CacheTags;
|
||||
public void SetTags(IReference[] newReference)
|
||||
|
Reference in New Issue
Block a user