Unity不会在协程中更新NavMeshAgent目标

时间:2015-11-15 22:03:50

标签: c# unity3d unity5 navmesh

我正在使用Unity 5中的AI脚本,该脚本主要由协同程序构建,以便在我的游戏中移动AI对象。它实际上工作得很好,并且这种方式与帧无关。我试图避免使类的Update函数混乱。

然而,我正在尝试创建一个名为wander的函数,其中AI挂起并在航点区域中随机选择几个Vector3并前往其中的每一个。在此之后,AI应该转到下一个航路点并基本上做同样的事情到无穷大。碰撞检测和所有类型适用于后续部件。我想首先修复导航部分。

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 个答案:

答案 0 :(得分:1)

考虑在航点上使用触发器,因为这对系统来说更加轻微。下面是一个示例,通过从父变换中获取所有 WayPoint 类型的子项,您可以轻松获得可变数量的航点。唯一使用的协同程序是空闲的,因为你说你不希望你的更新功能混乱。

我还暴露了一些额外的变量,包括最小和最大空闲时间,以及巡逻机会,应该将其设置为小于或等于1的值,表示巡逻与空闲的百分比。您还可以选择最小和最大数量的巡逻点,以便代理导航到。

RandomActivity 中,您还可以看到无限空闲的错误处理(父级下没有WayPoint子级)。代理也不会在其导航到的点列表中包含当前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);
    }
}

我知道这是一篇很老的帖子,但希望这对某人有帮助。