138 lines
6.6 KiB
C#
138 lines
6.6 KiB
C#
![]() |
using UnityEngine;
|
||
|
|
||
|
namespace BehaviorDesigner.Runtime.Tasks.Movement
|
||
|
{
|
||
|
[TaskDescription("Search for a target by combining the wander, within hearing range, and the within seeing range tasks using the Unity NavMesh.")]
|
||
|
[TaskCategory("Movement")]
|
||
|
[HelpURL("https://www.opsive.com/support/documentation/behavior-designer-movement-pack/")]
|
||
|
[TaskIcon("Assets/Behavior Designer Movement/Editor/Icons/{SkinColor}SearchIcon.png")]
|
||
|
public class Search : NavMeshMovement
|
||
|
{
|
||
|
[Tooltip("Minimum distance ahead of the current position to look ahead for a destination")]
|
||
|
public SharedFloat minWanderDistance = 20;
|
||
|
[Tooltip("Maximum distance ahead of the current position to look ahead for a destination")]
|
||
|
public SharedFloat maxWanderDistance = 20;
|
||
|
[Tooltip("The amount that the agent rotates direction")]
|
||
|
public SharedFloat wanderRate = 1;
|
||
|
[Tooltip("The minimum length of time that the agent should pause at each destination")]
|
||
|
public SharedFloat minPauseDuration = 0;
|
||
|
[Tooltip("The maximum length of time that the agent should pause at each destination (zero to disable)")]
|
||
|
public SharedFloat maxPauseDuration = 0;
|
||
|
[Tooltip("The maximum number of retries per tick (set higher if using a slow tick time)")]
|
||
|
public SharedInt targetRetries = 1;
|
||
|
[Tooltip("The field of view angle of the agent (in degrees)")]
|
||
|
public SharedFloat fieldOfViewAngle = 90;
|
||
|
[Tooltip("The distance that the agent can see")]
|
||
|
public SharedFloat viewDistance = 30;
|
||
|
[Tooltip("The LayerMask of the objects to ignore when performing the line of sight check")]
|
||
|
public LayerMask ignoreLayerMask = 1 << LayerMask.NameToLayer("Ignore Raycast");
|
||
|
[Tooltip("Should the search end if audio was heard?")]
|
||
|
public SharedBool senseAudio = true;
|
||
|
[Tooltip("How far away the unit can hear")]
|
||
|
public SharedFloat hearingRadius = 30;
|
||
|
[Tooltip("The raycast offset relative to the pivot position")]
|
||
|
public SharedVector3 offset;
|
||
|
[Tooltip("The target raycast offset relative to the pivot position")]
|
||
|
public SharedVector3 targetOffset;
|
||
|
[Tooltip("The LayerMask of the objects that we are searching for")]
|
||
|
public LayerMask objectLayerMask;
|
||
|
[Tooltip("Specifies the maximum number of colliders that the physics cast can collide with")]
|
||
|
public int maxCollisionCount = 200;
|
||
|
[Tooltip("Should the target bone be used?")]
|
||
|
public SharedBool useTargetBone;
|
||
|
[Tooltip("The target's bone if the target is a humanoid")]
|
||
|
public HumanBodyBones targetBone;
|
||
|
[Tooltip("Should a debug look ray be drawn to the scene view?")]
|
||
|
public SharedBool drawDebugRay;
|
||
|
[Tooltip("The further away a sound source is the less likely the agent will be able to hear it. " +
|
||
|
"Set a threshold for the the minimum audibility level that the agent can hear")]
|
||
|
public SharedFloat audibilityThreshold = 0.05f;
|
||
|
[Tooltip("The object that is found")]
|
||
|
public SharedGameObject returnedObject;
|
||
|
|
||
|
private float pauseTime;
|
||
|
private float destinationReachTime;
|
||
|
|
||
|
private Collider[] overlapColliders;
|
||
|
|
||
|
// Keep searching until an object is seen or heard (if senseAudio is enabled)
|
||
|
public override TaskStatus OnUpdate()
|
||
|
{
|
||
|
if (HasArrived()) {
|
||
|
// The agent should pause at the destination only if the max pause duration is greater than 0
|
||
|
if (maxPauseDuration.Value > 0) {
|
||
|
if (destinationReachTime == -1) {
|
||
|
destinationReachTime = Time.time;
|
||
|
pauseTime = Random.Range(minPauseDuration.Value, maxPauseDuration.Value);
|
||
|
}
|
||
|
if (destinationReachTime + pauseTime <= Time.time) {
|
||
|
// Only reset the time if a destination has been set.
|
||
|
if (TrySetTarget()) {
|
||
|
destinationReachTime = -1;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
TrySetTarget();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Detect if any objects are within sight
|
||
|
if (overlapColliders == null) {
|
||
|
overlapColliders = new Collider[maxCollisionCount];
|
||
|
}
|
||
|
returnedObject.Value = MovementUtility.WithinSight(transform, offset.Value, fieldOfViewAngle.Value, viewDistance.Value, overlapColliders, objectLayerMask, targetOffset.Value, ignoreLayerMask, useTargetBone.Value, targetBone, drawDebugRay.Value);
|
||
|
// If an object was seen then return success
|
||
|
if (returnedObject.Value != null) {
|
||
|
return TaskStatus.Success;
|
||
|
}
|
||
|
// Detect if any object are within audio range (if enabled)
|
||
|
if (senseAudio.Value) {
|
||
|
returnedObject.Value = MovementUtility.WithinHearingRange(transform, offset.Value, audibilityThreshold.Value, hearingRadius.Value, overlapColliders, objectLayerMask);
|
||
|
// If an object was heard then return success
|
||
|
if (returnedObject.Value != null) {
|
||
|
return TaskStatus.Success;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// No object has been seen or heard so keep searching
|
||
|
return TaskStatus.Running;
|
||
|
}
|
||
|
|
||
|
private bool TrySetTarget()
|
||
|
{
|
||
|
var direction = transform.forward;
|
||
|
var validDestination = false;
|
||
|
var attempts = targetRetries.Value;
|
||
|
var destination = transform.position;
|
||
|
while (!validDestination && attempts > 0) {
|
||
|
direction = direction + Random.insideUnitSphere * wanderRate.Value;
|
||
|
destination = transform.position + direction.normalized * Random.Range(minWanderDistance.Value, maxWanderDistance.Value);
|
||
|
validDestination = SamplePosition(destination);
|
||
|
attempts--;
|
||
|
}
|
||
|
if (validDestination) {
|
||
|
SetDestination(destination);
|
||
|
}
|
||
|
return validDestination;
|
||
|
}
|
||
|
|
||
|
// Reset the public variables
|
||
|
public override void OnReset()
|
||
|
{
|
||
|
base.OnReset();
|
||
|
|
||
|
minWanderDistance = 20;
|
||
|
maxWanderDistance = 20;
|
||
|
wanderRate = 2;
|
||
|
minPauseDuration = 0;
|
||
|
maxPauseDuration = 0;
|
||
|
targetRetries = 1;
|
||
|
fieldOfViewAngle = 90;
|
||
|
viewDistance = 30;
|
||
|
drawDebugRay = false;
|
||
|
senseAudio = true;
|
||
|
hearingRadius = 30;
|
||
|
audibilityThreshold = 0.05f;
|
||
|
}
|
||
|
}
|
||
|
}
|