/// --------------------------------------------- /// 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.AddOns.Shared.Runtime.Pathfinding; using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; using Opsive.BehaviorDesigner.Runtime.Utility; using Opsive.GraphDesigner.Runtime.Variables; using Opsive.Shared.Utility; using Unity.Entities; using UnityEngine; /// /// Base class for all Movement Pack tasks. /// [HideNameInTaskControl] [Category("Movement Pack")] public abstract class MovementBase : Action { #if UNITY_EDITOR private const string c_PathfinderTypeKey = "Opsive.BehaviorDesigner.AddOns.PathfinderType"; public static string PathfinderTypeKey => c_PathfinderTypeKey; #endif [Tooltip("Specifies the base pathfinding implementation that should be used.")] [SerializeField] [HideInInspector] protected Pathfinder m_Pathfinder; [Tooltip("Should the pathfinder be stopped when the task ends?")] public SharedVariable m_StopOnTaskEnd = true; public Pathfinder Pathfinder { get => m_Pathfinder; set => m_Pathfinder = value; } protected Vector3 Velocity { get => m_Pathfinder.Velocity; } protected float RemainingDistance { get => m_Pathfinder.RemainingDistance; } /// /// The task has been initialized. /// public override void OnAwake() { base.OnAwake(); if (m_Pathfinder == null) { m_Pathfinder = new NavMeshAgentPathfinder(); m_Pathfinder.Reset(); } m_Pathfinder.Initialize(gameObject); } /// /// Starts the task. /// public override void OnStart() { m_Pathfinder.OnStart(); } /// /// Set a new pathfinding destination. /// /// The destination to set. /// True if the destination is valid. protected virtual bool SetDestination(Vector3 destination) { return m_Pathfinder.SetDesination(destination); } /// /// Does the agent have a pathfinding path? /// /// True if the agent has a pathfinding path. protected bool HasPath() { return m_Pathfinder.HasPath(); } /// /// Returns true if the position is a valid pathfinding position. /// /// The position to sample. The position will be updated to the valid sampled position. /// True if the position is a valid pathfinding position. protected bool SamplePosition(ref Vector3 position) { return m_Pathfinder.SamplePosition(ref position); } /// /// Has the agent arrived at the destination? /// /// True if the agent has arrived at the destination. public bool HasArrived() { return m_Pathfinder.HasArrived(); } /// /// The task has ended. /// public override void OnEnd() { if (m_StopOnTaskEnd.Value) { m_Pathfinder.Stop(); } m_Pathfinder.OnEnd(); } /// /// Specifies the type of reflection that should be used to save the task. /// /// The index of the sub-task. This is used for the task set allowing each contained task to have their own save type. public override MemberVisibility GetSaveReflectionType(int index) { return MemberVisibility.None; } /// /// Returns the current task state. /// /// The DOTS world. /// The DOTS entity. /// The current task state. public override object Save(World world, Entity entity) { if (!m_BehaviorTree.IsNodeActive(true, m_RuntimeIndex)) return null; var saveData = new MovementSaveData(); if (m_Pathfinder.HasPath() && !m_Pathfinder.HasArrived()) { saveData.Destination = m_Pathfinder.Destination; saveData.HasDestination = true; } else { saveData.Destination = Vector3.zero; saveData.HasDestination = false; } return saveData; } /// /// Loads the previous task state. /// /// The previous task state. /// The DOTS world. /// The DOTS entity. public override void Load(object saveData, World world, Entity entity) { if (saveData == null) { return; } var movementSaveData = (MovementSaveData)saveData; if (movementSaveData.HasDestination) { m_Pathfinder.SetDesination(movementSaveData.Destination); } } /// /// The behavior tree has been stopped. /// /// Was the behavior tree paused. public override void OnBehaviorTreeStopped(bool paused) { base.OnBehaviorTreeStopped(paused); if (m_Pathfinder.HasPath()) { m_Pathfinder.Stop(); } } /// /// The behavior tree has been destroyed. /// public override void OnDestroy() { m_Pathfinder.Stop(); } /// /// Resets the task values back to their default. /// public override void Reset() { #if UNITY_EDITOR if (m_Pathfinder == null) { var defaultPathfinderType = UnityEditor.EditorPrefs.GetString(c_PathfinderTypeKey); if (!string.IsNullOrEmpty(defaultPathfinderType)) { var pathfinderType = TypeUtility.GetType(defaultPathfinderType); if (pathfinderType != null) { m_Pathfinder = System.Activator.CreateInstance(pathfinderType) as Pathfinder; } } // The NavMeshAgentPathfinder will always be available. if (m_Pathfinder == null) { m_Pathfinder = new NavMeshAgentPathfinder(); } } #endif m_Pathfinder.Reset(); m_StopOnTaskEnd = true; } /// /// Data structure for saving movement state. /// private class MovementSaveData { [Tooltip("The current destination that the agent is moving towards.")] public Vector3 Destination; [Tooltip("Indicates if the agent has a valid destination to move towards.")] public bool HasDestination; } } }