/// --------------------------------------------- /// Ultimate Character Controller /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.UltimateCharacterController.Demo.AI { using Opsive.UltimateCharacterController.Character.Abilities.AI; using Opsive.UltimateCharacterController.Character.Abilities.Items; using Opsive.UltimateCharacterController.Utility; using UnityEngine; /// /// Moves the agent to random positions within a circle. No pathfinding is done. /// public class AgentMovement : PathfindingMovement { [Tooltip("The radius of the position that the character can traverse to.")] [SerializeField] protected float m_Radius = 3; [Tooltip("The agent has arrived at the destination when the distance is less than the stopping distance.")] [SerializeField] protected float m_StoppingDistance = 0.1f; [Tooltip("The amount of time that the agent should wait at each destination.")] [SerializeField] protected MinMaxFloat m_RandomWait = new MinMaxFloat(0.2f, 0.7f); private Use m_UseAbility; private Vector3 m_Center; private Vector3 m_Destination; private Vector2 m_InputVector; private float m_WaitTime; private float m_ArriveTime; public override Vector2 InputVector { get { return m_InputVector; } } public override Vector3 DeltaRotation { get { return Vector3.zero; } } /// /// Initialize the default values. /// public override void Awake() { base.Awake(); m_UseAbility = m_CharacterLocomotion.GetAbility(); m_Center = m_Transform.position; DetermineDestination(); } /// /// Determines a new destination to move towards. /// private void DetermineDestination() { var randomPosition = Random.insideUnitSphere * m_Radius; randomPosition.y = 0; SetDestination(m_Center + randomPosition); } /// /// Sets the destination of the pathfinding agent. /// /// The position to move towards. /// True if the destination was set. public override bool SetDestination(Vector3 destination) { m_Destination = destination; m_ArriveTime = -1; return true; } /// /// Updates the ability. /// public override void Update() { // The Use ability uses root motion to control the movement so the pathfinding movement should not override that. if (m_UseAbility.IsActive) { return; } // Choose a new destination after the agent has arrived at the current destination. if (IsAtDestination()) { if (m_ArriveTime == -1) { m_WaitTime = m_RandomWait.RandomValue; m_ArriveTime = Time.time; } if (m_ArriveTime + m_WaitTime < Time.time) { DetermineDestination(); } } // The normalized velocity should be relative to the target rotation. var desiredMovement = (m_Destination - m_Transform.position).normalized; var velocity = Quaternion.Inverse(m_Transform.rotation) * desiredMovement; m_InputVector.x = velocity.x; m_InputVector.y = velocity.z; base.Update(); } /// /// Ensure the move direction is valid. /// public override void ApplyPosition() { if (IsAtDestination()) { // Prevent the character from jittering back and forth to land precisely on the target. var direction = m_Transform.InverseTransformPoint(m_Destination); var moveDirection = m_Transform.InverseTransformDirection(m_CharacterLocomotion.MoveDirection); if (Mathf.Abs(moveDirection.x) > Mathf.Abs(direction.x)) { moveDirection.x = direction.x; } if (Mathf.Abs(moveDirection.z) > Mathf.Abs(direction.z)) { moveDirection.z = direction.z; } m_CharacterLocomotion.MoveDirection = m_Transform.TransformDirection(moveDirection); } } /// /// Is the agent at the destination? /// /// True if the agent is at the destination. private bool IsAtDestination() { return (m_Destination - m_Transform.position).sqrMagnitude < m_StoppingDistance * m_StoppingDistance; } } }