BITKit/Packages/Common~/Scripts/Sensor/RangeSensor.cs

123 lines
4.1 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using Cysharp.Threading.Tasks;
using UnityEngine.Jobs;
using UnityEngine.Pool;
namespace BITKit.Sensors
{
public class RangeSensor : Sensor
{
[Header(Constant.Header.Settings)]
public float radius;
public int fov;
public bool requireSight;
[Header(Constant.Header.Settings)]
public LayerMask blockLayer;
[Header(Constant.Header.InternalVariables)]
FrameUpdate frameUpdater;
Collider[] colliders = new Collider[32];
RaycastHit[] hits;
Collider currentCollider;
Location location;
int length;
Vector3 dir;
float maxDistance;
public override IEnumerable<Transform> Get() => detecteds;
public override UniTask Excute()
{
if (frameUpdater)
{
if (maxDistance is 0)
{
maxDistance = Mathf.Max(subSensors.Select(x => x.GetDistance()).Append(radius).ToArray());
}
location.position = transform.position;
location.rotation = transform.rotation;
var list = ListPool<Transform>.Get();
for (int i = 0; i < Physics.OverlapSphereNonAlloc(location, maxDistance, colliders, detectLayer); i++)
{
currentCollider = colliders[i];
if (IsValid(currentCollider))
list.Add(currentCollider.transform);
else
{
foreach (var sensor in subSensors)
{
if (sensor.IsValid(currentCollider))
{
list.Add(currentCollider.transform);
break;
}
}
}
}
detecteds = list.ToArray();
list.Clear();
ListPool<Transform>.Release(list);
}
return UniTask.CompletedTask;
}
public override bool IsValid(Collider collider)
{
if (ignoreColliders.Contains(collider)
|| CheckFov(ref collider) is false
|| CheckSight(ref collider) is false
|| CheckDistance(ref collider) is false
)
{
return false;
}
else return true;
}
public override float GetDistance() => radius;
bool CheckFov(ref Collider collider)
{
if (fov is not 0)
{
var _dir = collider.transform.position - transform.position;
if (_dir.sqrMagnitude > 0)
{
var dir = Quaternion.LookRotation(_dir);
if (Vector3.Dot(transform.forward, _dir) > 0 && fov > Quaternion.Angle(transform.rotation, dir))
{
return true;
}
else return false;
}
else return true;
}
else
{
return true;
}
}
bool CheckSight(ref Collider collider)
{
if (requireSight)
{
length = Physics.RaycastNonAlloc(
location.position,
collider.transform.position - location,
hits,
Vector3.Distance(location, collider.transform.position),
blockLayer
);
if (length > 0)
{
if (hits[0].collider == collider)
{
return true;
}
}
else return true;
}
return false;
}
bool CheckDistance(ref Collider collider)
{
return Vector3.Distance(collider.transform.position, transform.position) <= radius;
}
}
}