OldBlueWater/BlueWater/Assets/Behavior Designer Movement/Scripts/Tasks/CanSeeObject.cs

198 lines
11 KiB
C#
Raw Normal View History

2023-09-26 06:12:44 +00:00
using UnityEngine;
namespace BehaviorDesigner.Runtime.Tasks.Movement
{
[TaskDescription("Check to see if the any objects are within sight of the agent.")]
[TaskCategory("Movement")]
[HelpURL("https://www.opsive.com/support/documentation/behavior-designer-movement-pack/")]
[TaskIcon("c3873913d6f08e44d8f24b80257edf45", "7f2d1486b1b44ec4b8c213df246534c5")]
public class CanSeeObject : Conditional
{
[Tooltip("Should the 2D version be used?")]
[UnityEngine.Serialization.FormerlySerializedAs("usePhysics2D")]
public bool m_UsePhysics2D;
[Tooltip("Specifies the type of detection that should be used.")]
public SharedDetectionMode m_DetectionMode = DetectionMode.Object | DetectionMode.ObjectList | DetectionMode.Tag | DetectionMode.LayerMask;
[Tooltip("If using the Object detection mode, specifies the target object that is being searched.")]
[UnityEngine.Serialization.FormerlySerializedAs("targetObject")]
public SharedGameObject m_TargetObject;
[Tooltip("If using the Target Objects detection mode, specifies the objects that are being searched.")]
[UnityEngine.Serialization.FormerlySerializedAs("targetObjects")]
public SharedGameObjectList m_TargetObjects;
[Tooltip("If using the Tag detection mode, specifies the tag of the objects that are being searched.")]
[UnityEngine.Serialization.FormerlySerializedAs("targetTag")]
public SharedString m_TargetTag;
[Tooltip("If using the LayerMask detection mode, specifies the LayerMask of the objects that are being searched.")]
[UnityEngine.Serialization.FormerlySerializedAs("objectLayerMask")]
public SharedLayerMask m_TargetLayerMask;
[Tooltip("If using the LayerMask detection mode, specifies the maximum number of colliders that the physics cast can collide with.")]
[UnityEngine.Serialization.FormerlySerializedAs("maxCollisionCount")]
public int m_MaxCollisionCount = 200;
[Tooltip("The LayerMask of the objects to ignore when performing the line of sight check.")]
[UnityEngine.Serialization.FormerlySerializedAs("ignoreLayerMask")]
public LayerMask m_IgnoreLayerMask;
[Tooltip("The field of view angle of the agent (in degrees).")]
[UnityEngine.Serialization.FormerlySerializedAs("fieldOfViewAngle")]
public SharedFloat m_FieldOfViewAngle = 90;
[Tooltip("The distance that the agent can see.")]
[UnityEngine.Serialization.FormerlySerializedAs("viewDistance")]
public SharedFloat m_ViewDistance = 1000;
[Tooltip("The raycast offset relative to the pivot position.")]
[UnityEngine.Serialization.FormerlySerializedAs("offset")]
public SharedVector3 m_Offset;
[Tooltip("The target raycast offset relative to the pivot position.")]
[UnityEngine.Serialization.FormerlySerializedAs("targetOffset")]
public SharedVector3 m_TargetOffset;
[Tooltip("The offset to apply to 2D angles.")]
[UnityEngine.Serialization.FormerlySerializedAs("angleOffset2D")]
public SharedFloat m_AngleOffset2D;
[Tooltip("Should the target bone be used?")]
[UnityEngine.Serialization.FormerlySerializedAs("useTargetBone")]
public SharedBool m_UseTargetBone;
[Tooltip("The target's bone if the target is a humanoid.")]
[UnityEngine.Serialization.FormerlySerializedAs("targetBone")]
public SharedHumanBodyBones m_TargetBone;
[Tooltip("Should a debug look ray be drawn to the scene view?")]
[UnityEngine.Serialization.FormerlySerializedAs("drawDebugRay")]
public SharedBool m_DrawDebugRay;
[Tooltip("Should the agent's layer be disabled before the Can See Object check is executed?")]
[UnityEngine.Serialization.FormerlySerializedAs("disableAgentColliderLayer")]
public SharedBool m_DisableAgentColliderLayer;
[Tooltip("The object that is within sight.")]
[UnityEngine.Serialization.FormerlySerializedAs("returnedObject")]
public SharedGameObject m_ReturnedObject;
private GameObject[] m_AgentColliderGameObjects;
private int[] m_OriginalColliderLayer;
private Collider[] m_OverlapColliders;
private Collider2D[] m_Overlap2DColliders;
private int m_IgnoreRaycastLayer = LayerMask.NameToLayer("Ignore Raycast");
// Returns success if an object was found otherwise failure
public override TaskStatus OnUpdate()
{
m_ReturnedObject.Value = null;
// The collider layers on the agent can be set to ignore raycast to prevent them from interferring with the raycast checks.
if (m_DisableAgentColliderLayer.Value) {
if (m_AgentColliderGameObjects == null) {
if (m_UsePhysics2D) {
var colliders = gameObject.GetComponentsInChildren<Collider2D>();
m_AgentColliderGameObjects = new GameObject[colliders.Length];
for (int i = 0; i < m_AgentColliderGameObjects.Length; ++i) {
m_AgentColliderGameObjects[i] = colliders[i].gameObject;
}
} else {
var colliders = gameObject.GetComponentsInChildren<Collider>();
m_AgentColliderGameObjects = new GameObject[colliders.Length];
for (int i = 0; i < m_AgentColliderGameObjects.Length; ++i) {
m_AgentColliderGameObjects[i] = colliders[i].gameObject;
}
}
m_OriginalColliderLayer = new int[m_AgentColliderGameObjects.Length];
}
// Change the layer. Remember the previous layer so it can be reset after the check has completed.
for (int i = 0; i < m_AgentColliderGameObjects.Length; ++i) {
m_OriginalColliderLayer[i] = m_AgentColliderGameObjects[i].layer;
m_AgentColliderGameObjects[i].layer = m_IgnoreRaycastLayer;
}
}
if ((m_DetectionMode.Value & DetectionMode.Object) != 0 && m_TargetObject.Value != null) {
if (m_UsePhysics2D) {
m_ReturnedObject.Value = MovementUtility.WithinSight2D(transform, m_Offset.Value, m_FieldOfViewAngle.Value, m_ViewDistance.Value, m_TargetObject.Value, m_TargetOffset.Value, m_AngleOffset2D.Value, m_IgnoreLayerMask, m_UseTargetBone.Value, m_TargetBone.Value, m_DrawDebugRay.Value);
} else {
m_ReturnedObject.Value = MovementUtility.WithinSight(transform, m_Offset.Value, m_FieldOfViewAngle.Value, m_ViewDistance.Value, m_TargetObject.Value, m_TargetOffset.Value, m_IgnoreLayerMask, m_UseTargetBone.Value, m_TargetBone.Value, m_DrawDebugRay.Value);
}
}
if (m_ReturnedObject.Value == null && (m_DetectionMode.Value & DetectionMode.ObjectList) != 0) {
var minAngle = Mathf.Infinity;
for (int i = 0; i < m_TargetObjects.Value.Count; ++i) {
GameObject obj;
if ((obj = MovementUtility.WithinSight(transform, m_Offset.Value, m_FieldOfViewAngle.Value, m_ViewDistance.Value, m_TargetObjects.Value[i], m_TargetOffset.Value, m_UsePhysics2D, m_AngleOffset2D.Value, out var angle, m_IgnoreLayerMask, m_UseTargetBone.Value, m_TargetBone.Value, m_DrawDebugRay.Value)) != null) {
// This object is within sight. Set it to the objectFound GameObject if the angle is less than any of the other objects
if (angle < minAngle) {
minAngle = angle;
m_ReturnedObject.Value = obj;
}
}
}
}
if (m_ReturnedObject.Value == null && (m_DetectionMode.Value & DetectionMode.Tag) != 0 && !string.IsNullOrEmpty(m_TargetTag.Value)) {
var targets = GameObject.FindGameObjectsWithTag(m_TargetTag.Value);
if (targets != null) {
var minAngle = Mathf.Infinity;
for (int i = 0; i < targets.Length; ++i) {
GameObject obj;
if ((obj = MovementUtility.WithinSight(transform, m_Offset.Value, m_FieldOfViewAngle.Value, m_ViewDistance.Value, targets[i], m_TargetOffset.Value, m_UsePhysics2D, m_AngleOffset2D.Value, out var angle, m_IgnoreLayerMask, m_UseTargetBone.Value, m_TargetBone.Value, m_DrawDebugRay.Value)) != null) {
// This object is within sight. Set it to the objectFound GameObject if the angle is less than any of the other objects
if (angle < minAngle) {
minAngle = angle;
m_ReturnedObject.Value = obj;
}
}
}
}
}
if (m_ReturnedObject.Value == null && (m_DetectionMode.Value & DetectionMode.LayerMask) != 0) {
if (m_UsePhysics2D) {
if (m_Overlap2DColliders == null) {
m_Overlap2DColliders = new Collider2D[m_MaxCollisionCount];
}
m_ReturnedObject.Value = MovementUtility.WithinSight2D(transform, m_Offset.Value, m_FieldOfViewAngle.Value, m_ViewDistance.Value, m_Overlap2DColliders, m_TargetLayerMask.Value, m_TargetOffset.Value, m_AngleOffset2D.Value, m_IgnoreLayerMask, m_DrawDebugRay.Value);
} else {
if (m_OverlapColliders == null) {
m_OverlapColliders = new Collider[m_MaxCollisionCount];
}
m_ReturnedObject.Value = MovementUtility.WithinSight(transform, m_Offset.Value, m_FieldOfViewAngle.Value, m_ViewDistance.Value, m_OverlapColliders, m_TargetLayerMask.Value, m_TargetOffset.Value, m_IgnoreLayerMask, m_UseTargetBone.Value, m_TargetBone.Value, m_DrawDebugRay.Value);
}
}
// Restore the original layers.
if (m_DisableAgentColliderLayer.Value) {
for (int i = 0; i < m_AgentColliderGameObjects.Length; ++i) {
m_AgentColliderGameObjects[i].layer = m_OriginalColliderLayer[i];
}
}
if (m_ReturnedObject.Value != null) {
return TaskStatus.Success;
}
// An object is not within sight so return failure
return TaskStatus.Failure;
}
// Reset the public variables
public override void OnReset()
{
m_DetectionMode = DetectionMode.Object | DetectionMode.ObjectList | DetectionMode.Tag | DetectionMode.LayerMask;
m_FieldOfViewAngle = 90;
m_ViewDistance = 1000;
m_Offset = Vector3.zero;
m_TargetOffset = Vector3.zero;
m_AngleOffset2D = 0;
m_TargetTag = "";
m_DrawDebugRay = false;
m_IgnoreLayerMask = 1 << LayerMask.NameToLayer("Ignore Raycast");
}
// Draw the line of sight representation within the scene window
public override void OnDrawGizmos()
{
MovementUtility.DrawLineOfSight(Owner.transform, m_Offset.Value, m_FieldOfViewAngle.Value, m_AngleOffset2D.Value, m_ViewDistance.Value, m_UsePhysics2D);
}
public override void OnBehaviorComplete()
{
MovementUtility.ClearCache();
}
}
}