1

我正在 Unity 5 中编写一个 AI 脚本,该脚本主要由协程构成,用于在我的游戏中移动 AI 对象。它实际上工作得很好,并且以这种方式独立于框架。我试图避免混淆Update类的功能。

但是,我正在尝试创建一个名为wander 的函数,人工智能在其中徘徊并随机选择Vector3航点区域中的几个s 并前往每个s。之后,人工智能应该去下一个航路点,基本上做同样的事情到无穷远。碰撞检测和所有这些都是为了后面的部分。我想先修复导航部分。

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如果我将函数与idlein 中的函数交换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,但没有运气,因为它会永远留在那个循环中,因为没有路径可以去。

我可能在这里遗漏了一些东西,或者在这种情况下我的逻辑是错误的。希望有人可以给我一点推动或一些额外的调试选项,因为我不知道为什么目的地没有在我的漫游功能中更新。

4

1 回答 1

1

考虑改为在航路点上使用触发器,因为这对系统的负担会更少。下面是一个示例,通过从父变换中获取所有WayPoint类型的子项,您可以轻松地拥有可变数量的航点。唯一使用的协程是空闲的,因为你说你不希望你的更新函数混乱。

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

RandomActivity中,您还可以看到无限空闲(父级下没有 WayPoint 子级)的错误处理。代理也不会在其要导航到的点列表中包含当前 Waypoint,并且不会导航到在其当前巡逻期间已导航到的点(每次将元素添加到m_targets时,它都会从m_availableTargets中删除)。

代理控制器.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 的标签,以便轻松区分游戏中的敌人和任何其他跳跳虎。

航点.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);
    }
}

我知道这是一个旧帖子,但希望这对某人有所帮助。

于 2016-03-01T11:42:28.113 回答