Unity не обновляет пункт назначения NavMeshAgent в сопрограмме

Я работаю над сценарием ИИ в Unity 5, в основном построенным из сопрограмм для перемещения объекта ИИ в моей игре. Это на самом деле работает довольно хорошо и таким образом не зависит от кадров. Я стараюсь не загромождать Update функция класса.

Тем не менее, я пытаюсь создать функцию с именем wander, где ИИ зависает и случайным образом выбирает пару Vector3s в области точки пути и проезжайте к каждому из них. После этого ИИ должен перейти к следующей точке и сделать то же самое в бесконечности. Обнаружение столкновений и все такое для последующих частей. Я хочу сначала исправить навигационную часть.

void Start(){
    agent = GetComponent<NavMeshAgent> ();

    collisionRange = GetComponent<CapsuleCollider> ();
    collisionRange.radius = detectionRadius;

    if (waypoints.Length > 0) {
        StartCoroutine (patrol ());
    } else {
        StartCoroutine (idle (idleTime));
    }
}

IEnumerator patrol(){
    agent.SetDestination(waypoints[waypointIndex].position);

    Debug.Log ("Patrol started, moving to " + agent.destination);
    while (agent.pathPending) {
        Debug.Log ("not having path");
        yield return null;
    }   

    Debug.Log ("I have a path, distance is " + agent.remainingDistance);

    while (float.Epsilon < agent.remainingDistance) {
        //Debug.Log ("Moving...");
        yield return null;
    }

    StartCoroutine (nextWaypoint ());
}

IEnumerator idle(int time){
    Debug.Log ("Idleing for "+ time + " seconds");
    agent.Stop ();

    yield return new WaitForSeconds(time);

    if(waypoints.Length > 2){
        agent.Resume ();
        StartCoroutine(patrol());
    }
}

IEnumerator wander(){
    agent.Stop ();
    Debug.Log ("Going to look around here for a while.");

    Vector3[] points = new Vector3[wanderPoints];

    for (int i = 0; i < wanderPoints; i++) {
        agent.Stop ();
        Vector3 point = Random.insideUnitSphere * wanderRadius;
        point.y = transform.position.y;

        points [i] = point;
    }

    agent.ResetPath ();
    agent.SetDestination(points[0]);
    agent.Resume ();

    Debug.Log ("point: " + points [0]);
    Debug.Log ("Destination: " + agent.destination);

    while (float.Epsilon < agent.remainingDistance) {
        Debug.Log ("Moving...");
        yield return null;
    }

    //StartCoroutine(patrol());
    yield return null;

}

IEnumerator nextWaypoint(){
    Debug.Log ("Arrived at my waypoint at " + transform.position);

    if (waypointIndex < waypoints.Length -1) {
        waypointIndex +=1;
    } else {
        waypointIndex = 0;
    }

    StartCoroutine(wander ());
    yield return null;
}

Если я поменяю wander функция с idle функция в nextWaypointвсе работает как положено, но этот бит никогда не будет работать:

agent.ResetPath ();
agent.SetDestination(points[0]);
agent.Resume ();

Debug.Log ("point: " + points [0]);
Debug.Log ("Destination: " + agent.destination);

Это немного тестового кода (установка вручную только 1 позиции point[0], но он никогда не поедет к этому месту назначения. SetDestination никогда не будет обновляться до точек, которые я хочу установить. Я попытался вычислить пути (NavMeshPath) заранее и все, кроме пути назначения не будет изменяться или сбрасываться достаточно странно. Я также имел while (float.Epsilon < agent.remainingDistance) петля в wander функционировать также, но без удачи, так как он останется в этом цикле навсегда, так как пути нет.

Я могу что-то здесь упустить, или моя логика в этом случае неверна. Надеюсь, кто-нибудь может дать мне небольшой толчок или, возможно, некоторые дополнительные параметры отладки, потому что я понятия не имею, почему пункт назначения не обновляется в моей функции блуждания.

1 ответ

Попробуйте вместо этого использовать триггеры на путевых точках, так как это будет еще более обременительным для системы. Ниже приведен пример, который позволит вам легко иметь переменное число путевых точек, получая всех потомков типа WayPoint из родительского преобразования. Единственный Coroutine, который используется, предназначен для простоя, поскольку вы сказали, что не хотите, чтобы ваша функция Update была загромождена.

Я также раскрыл несколько дополнительных переменных, в том числе минимальное и максимальное время простоя и шанс патрулирования, который должен быть установлен на значение, меньшее или равное 1, представляющее процентную вероятность патрулирования против простоя. Вы также можете выбрать минимальное и максимальное количество точек патрулирования, по которым агент будет перемещаться.

В RandomActivity вы также можете видеть, что есть обработка ошибок для бесконечно простоя (нет дочерних элементов WayPoint под родительским). Агент также не будет включать текущую путевую точку в свой список точек, к которым он будет перемещаться, и не будет переходить к точке, к которой уже осуществлялась навигация во время текущего патрулирования (каждый раз, когда элемент добавляется в m_targets, он удаляется из m_availableTargets).

AgentController.cs

[RequireComponent(typeof(NavMeshAgent))]
public class AgentController : MonoBehaviour
{
    [SerializeField]
    private Transform       m_waypointParent;
    [SerializeField]
    private float           m_minIdle;
    [SerializeField]
    private float           m_maxIdle;
    [SerializeField]
    private float           m_patrolChance;
    [SerializeField]
    private int             m_minPatrolPoints;
    [SerializeField]
    private int             m_maxPatrolPoints;

    private Waypoint[]      m_waypoints;
    private List<Waypoint>  m_availableTargets;
    private List<Waypoint>  m_targets;
    private Waypoint        m_tempWaypoint;
    private int             m_currentTargetIndex;
    private NavMeshAgent    m_navMeshAgent;

    public void Start()
    {
        m_waypoints     = m_waypointParent.GetComponentsInChildren<Waypoint>();
        m_targets       = new List<Waypoint>();
        m_navMeshAgent  = GetComponent<NavMeshAgent>();
        RandomActivity();
    }

    private void RandomActivity()
    {
        if (m_waypoints.Length == 0)
        {
            Debug.Log("Enemy will idle forever (no waypoints found)");
            StartCoroutine(Idle(Random.Range(m_minIdle, m_maxIdle)));
            return;
        }

        if(Random.Range(0f, 1f) <= m_patrolChance)
        {
            //Available waypoints
            m_availableTargets = new List<Waypoint>(m_waypoints);

            //Remove currentpoint
            if(m_targets.Count > 0)
                m_availableTargets.Remove(m_targets[m_targets.Count - 1]);

            //Reset list
            m_targets.Clear();
            m_currentTargetIndex = -1;

            //Add patrol points
            for (int i = 0; i < Random.Range(m_minPatrolPoints, m_maxPatrolPoints + 1); i++)
            {
                m_tempWaypoint = m_availableTargets[Random.Range(0, m_availableTargets.Count)];
                m_targets.Add(m_tempWaypoint);
                m_availableTargets.Remove(m_tempWaypoint);
            }

            NextWaypoint(null);
        }
        else
            StartCoroutine(Idle(Random.Range(m_minIdle, m_maxIdle)));
    }

    public void NextWaypoint(Waypoint p_waypoint)
    {
        //Collided with current waypoint target?
        if ((m_currentTargetIndex == -1) || (p_waypoint == m_targets[m_currentTargetIndex]))
        {
            m_currentTargetIndex++;

            if (m_currentTargetIndex == m_targets.Count)
                RandomActivity();
            else
            {
                Debug.Log("Target: " + (m_currentTargetIndex + 1) + "/" + m_targets.Count + " (" + m_targets[m_currentTargetIndex].transform.position + ")");
                m_navMeshAgent.SetDestination(m_targets[m_currentTargetIndex].transform.position);
            }
        }
    }

    private IEnumerator Idle(float p_time)
    {
        Debug.Log("Idling for " + p_time + "s");
        yield return new WaitForSeconds(p_time);
        RandomActivity();
    }
}

Обратите внимание, что для этого я создал тег Enemy, чтобы легко отличать врагов от любых других тигров в вашей игре.

WayPoint.cs

[RequireComponent(typeof(BoxCollider))]
public class Waypoint : MonoBehaviour
{
    public void OnTriggerEnter(Collider p_collider)
    {
        if (p_collider.tag == "Enemy")
            p_collider.GetComponent<AgentController>().NextWaypoint(this);
    }
}

Я знаю, что это старый пост, но надеюсь, что это кому-то поможет.

Другие вопросы по тегам