/// --------------------------------------------- /// Movement Pack for Behavior Designer Pro /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.BehaviorDesigner.AddOns.MovementPack.Runtime.Tasks { using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.GraphDesigner.Runtime; using Opsive.GraphDesigner.Runtime.Variables; using UnityEngine; [Opsive.Shared.Utility.Description("Evade the specified target. Evade will predict where the target is headed based on the Look Ahead Distance. " + "Evade is similar to Flee except Evade uses the target's velocity to predict where to move towards whereas Flee only uses the target's position.")] [NodeIcon("8e0f6d1ef072a8d468943f37b3453bd9", "cfa59bcf26297d44e8b774d0568c996c")] public class Evade : MovementBase { [Tooltip("The GameObject that the agent is evading from.")] [SerializeField] protected SharedVariable m_Target; [Tooltip("The agent has evaded when the magnitude is greater than this value")] [SerializeField] protected SharedVariable m_EvadeDistance = 10; [Tooltip("The distance to look ahead when evading")] [SerializeField] protected SharedVariable m_LookAheadDistance = 5; [Tooltip("Specifies how far to predict the distance ahead of the target. Lower values indicate that less distance should be predicated.")] [SerializeField] protected SharedVariable m_DistancePrediction = 20; [Tooltip("Specifies the multiplier for predicting the look ahead distance.")] [SerializeField] protected SharedVariable m_DistancePredictionMultiplier = 20; [Tooltip("The maximum number of interations that the position should be set")] [SerializeField] [Range(0, 100)] protected int m_MaxInterations = 1; private Vector3 m_TargetPosition; /// /// The task has started. /// public override void OnStart() { base.OnStart(); if (m_MaxInterations == 0) { Debug.LogWarning("Warning: Max iterations must be greater than 0."); m_MaxInterations = 1; } if (m_Target == null) { Debug.LogError("Error: A target must be set on the Evade ability."); return; } m_TargetPosition = m_Target.Value.transform.position; } /// /// Updates the flee destination. /// /// Success when the agent has fleed. public override TaskStatus OnUpdate() { if (m_Target.Value == null) { return TaskStatus.Failure; } if (Vector3.Magnitude(transform.position - m_Target.Value.transform.position) > m_EvadeDistance.Value) { return TaskStatus.Success; } return SetTargetDestination() ? TaskStatus.Running : TaskStatus.Failure; } /// /// Sets the target destination. /// /// True if the target destination is valid. private bool SetTargetDestination() { var predictedDestination = GetPredictedDestination(); var validDestination = false; var interation = 0; do { var targetDestination = transform.position + (transform.position - predictedDestination).normalized * m_LookAheadDistance.Value * ((m_MaxInterations - interation) / m_MaxInterations); validDestination = SetDestination(targetDestination); } while (!validDestination && interation < m_MaxInterations - 1); return interation < m_MaxInterations; } /// /// Returns the predicted target destination. /// /// The predicted target destination. private Vector3 GetPredictedDestination() { // Calculate the current distance to the target and the current speed. var distance = (m_Target.Value.transform.position - transform.position).magnitude; var velocityMagnitude = m_Pathfinder.Velocity.magnitude; var futurePrediction = 0f; // Set the future prediction to max prediction if the speed is too small to give an accurate prediction. if (velocityMagnitude <= distance / m_DistancePrediction.Value) { futurePrediction = m_DistancePrediction.Value; } else { futurePrediction = (distance / velocityMagnitude) * m_DistancePredictionMultiplier.Value; } // Predict the future by taking the velocity of the target and multiply it by the future prediction. var prevTargetPosition = m_TargetPosition; m_TargetPosition = m_Target.Value.transform.position; return m_TargetPosition + (m_TargetPosition - prevTargetPosition) * futurePrediction; } /// /// Sets the destination. Returns false if the destination isn't valid. /// /// The target destination. /// True if the destination was successfully set. protected override bool SetDestination(Vector3 destination) { if (!SamplePosition(ref destination)) { return false; } return base.SetDestination(destination); } /// /// Resets the task values back to their default. /// public override void Reset() { base.Reset(); m_Target = null; m_EvadeDistance = 10; m_LookAheadDistance = 5; m_DistancePrediction = 20; m_DistancePredictionMultiplier = 20; m_MaxInterations = 1; } } }