Unity3d NavMeshAgent.isOnNavMesh在特定功能中变为假 [英] Unity3d NavMeshAgent.isOnNavMesh becomes false in specific function

查看:367
本文介绍了Unity3d NavMeshAgent.isOnNavMesh在特定功能中变为假的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我更改了标题以反映澄清信息的添加。



我正在关注[Unity Tutorial] [1],当需要测试玩家的单击控件时,Unity给了我一个错误:



SetDestination只能调用



据我所知,我的代理处于活动状态,并且在navMesh上,所以这不仅仅是有点混乱。我尝试重新烘焙navMesh并重新定位两个都不起作用的代理。



到目前为止,我发现的所有问题都等于问问问问者是不是navMesh完全是这样...是的...不是很有帮助。有关如何解决此问题的任何建议,我们将不胜感激。



编辑:
我刚刚添加了一个快速的 Debug.Log(agent.isOnNavMesh); 到我的代码&低和因为它的评估结果为 true

  private void Start()
{
Debug.Log(agent.isOnNavMesh); //评估* true *
agent.updateRotation = false;

inputHoldWait = new WaitForSeconds(inputHoldDelay);

destinationPosition = transform.position;
}

EDIT-2
相同的 Debug.Log(agent.isOnNavMesh); 进入我的公共无效OnGroundClick fxn&点击后评估为 false 。开始引起人们的困惑。



这由Unity事件系统调用:

  public void OnGroundClick(BaseEventData data)
{
Debug.Log(agent.isOnNavMesh); //评估* FALSE *
PointerEventData pData =(PointerEventData)data;
NavMeshHit命中;

//单击世界位置,命中信息,采样距离,navMesh区域以使用
if(NavMesh.SamplePosition(pData.pointerCurrentRaycast.worldPosition,out hit,navMeshSampleDistance,NavMesh.AllAreas))
{
destinationPosition = hit.position;
}
其他
{
destinationPosition = pData.pointerCurrentRaycast.worldPosition;
}

//将代理指定为目的地
agent.SetDestination(destinationPosition);
agent.isStopped = false;
}

EDIT-3
Debug.Log(agent.isOnNavMesh); 进入 private void Update(),其评估结果为 true ,并且即使在Click调用 public void OnGroundClick 后仍会继续这样做。



OnGroundClick 开始时禁用然后启用代理不会影响这种情况



尽管我仍然无所适从,我至少更接近解决方案了!

以下是完整上下文中的代码:

p>

 使用System.Collections; 
使用System.Collections.Generic;
使用UnityEngine;
使用UnityEngine.EventSystems;
使用UnityEngine.AI;

公共类PlayerMovement:MonoBehaviour
{
public Animator动画师; //参考动画师
公共NavMeshAgent代理; //参考NavMeshAgent
public float inputHoldDelay = 0.5f; //延迟玩家与可互动对象
互动时控制角色的能力public float turnSpeedThreshold = 0.5f; //角色转
之前的最低速度public float speedDampTime = 0.1f; //字符速率加速
公共浮点slowingSpeed = 0.175f; //负运算符的字符速率
public float turnSmoothing = 15; //字符转速


private WaitForSeconds inputHoldWait; //协程等待计时器,以在与可交互对象
交互时延迟玩家的输入; private Vector3 destinationPosition; //玩家为代理商指定的目的地


private const float stopDistanceProportion = 0.1f;
private const float navMeshSampleDistance = 4f;


private readonly int hashSpeedParam = Animator.StringToHash( Speed);


private void Start()
{
Debug.Log(agent.gameObject.name); //是玩家对象
Debug.Log(agent.isOnNavMesh); //评估* true *
agent.updateRotation = false;

inputHoldWait = new WaitForSeconds(inputHoldDelay);

destinationPosition = transform.position;
}


private void OnAnimatorMove()
{
//速度=时间上的距离,其中Distance =帧之间的位置变化&时间=帧之间的时间
agent.velocity = animator.deltaPosition / Time.deltaTime;
}


private void Update()
{
Debug.Log(agent.isOnNavMesh); //计算* true *
//如果路径挂起,如果(agent.pathPending)
返回,则不执行


浮动速度= agent.desiredVelocity.magnitude;

if(agent.remainingDistance< = agent.stoppingDistance * stopDistanceProportion)
{
停止(超出速度);
}
else if(agent.remainingDistance< = agent.stoppingDistance)
{
Slowing(agent.remainingDistance,out speed);
}
else if(speed> turnSpeedThreshold)
{
Moving();
}

animator.SetFloat(hashSpeedParam,speed,speedDampTime,Time.deltaTime);
}


private void停止(浮动速度)
{
agent.isStopped = true;
transform.position = destinationPosition;
速度= 0.0f;
}


private void减速(浮动距离ToDestination,超出浮动速度)
{
agent.isStopped = true;
transform.position = Vector3.MoveTowards(transform.position,destinationPosition,slowingSpeed * Time.deltaTime);

浮动比例距离= 1f-distanceToDestination / agent.stoppingDistance;
speed = Mathf.Lerp(slowingSpeed,0,比例距离);
}


private void Moving()
{
Quaternion targetRotation = Quaternion.LookRotation(agent.desiredVelocity);
transform.rotation =四元数.Lerp(transform.rotation,targetRotation,turnSmoothing * Time.deltaTime);
}


public void OnGroundClick(BaseEventData data)
{
agent.enabled = false; //禁用然后启用代理...
agent.enabled = true; //不做任何更改。

Debug.Log(agent.gameObject.name); //是玩家对象
Debug.Log(agent.isOnNavMesh); //评估* FALSE *

PointerEventData pData =(PointerEventData)data;
NavMeshHit命中;

//单击世界位置,命中信息,采样距离,navMesh区域以使用
if(NavMesh.SamplePosition(pData.pointerCurrentRaycast.worldPosition,out hit,navMeshSampleDistance,NavMesh.AllAreas))
{
destinationPosition = hit.position;
}
其他
{
destinationPosition = pData.pointerCurrentRaycast.worldPosition;
}

//将代理指定为目的地
agent.SetDestination(destinationPosition);
agent.isStopped = false;
}
}


解决方案

PlayerMovement 组件有两个实例,分别引用了代理的两个不同实例!



一个实例是一个 agent.isOnNavMesh true ,并且可以在开始更新中正常运行。另一个实例引用了另一个 agent ,其中 agent.isOnNavMesh false ,但这是正在调用其 OnGroundclick 的那个。



我们可以说是因为 GetInstanceID() Update OnGroundClick 中返回不同的值。



这里的解决方案是确保事件系统中注册的 OnGroundClick 是正确实例中的 PlayerMovement


I've changed the Title to reflect the addition of clarifying info.

I'm following a [Unity Tutorial][1] and when it came time to test the player click controls Unity gave me an error:

"SetDestination" can only be called on an active agent that has been placed on a NavMesh.

As far as I can tell my agent is active and on the navMesh so this is more than a little confusing. I've tried rebaking the navMesh and repositioning the agent neither of which has worked.

All of the questions I've found thus far have amounted to the asker not having a navMesh at all so... yeah... not very helpful. Any suggestions on how to resolve this while be appreciated.

EDIT: I just added a quick Debug.Log(agent.isOnNavMesh); to my code & low & behold it evaluates to true. Peek confusion.

private void Start()
    {
        Debug.Log(agent.isOnNavMesh);       //Evaluates *true*
        agent.updateRotation = false;

        inputHoldWait = new WaitForSeconds(inputHoldDelay);

        destinationPosition = transform.position;
    }

EDIT-2 Put the same Debug.Log(agent.isOnNavMesh); into my public void OnGroundClick fxn & it evaluates to false after tha click. Begin intrigued confusion.

This is called by a Unity Event System:

public void OnGroundClick(BaseEventData data)
{
    Debug.Log(agent.isOnNavMesh);       //Evaluates *FALSE*
    PointerEventData pData = (PointerEventData)data;
    NavMeshHit hit;

        //Click World Position, hit info, sample distance, navMesh areas to use
    if (NavMesh.SamplePosition(pData.pointerCurrentRaycast.worldPosition, out hit, navMeshSampleDistance, NavMesh.AllAreas))
    {
        destinationPosition = hit.position;
    }
    else
    {
        destinationPosition = pData.pointerCurrentRaycast.worldPosition;
    }

    //give the agent it's destination
    agent.SetDestination(destinationPosition);
    agent.isStopped = false;
}

EDIT-3 I put Debug.Log(agent.isOnNavMesh); in to private void Update(), it evaluated to true and continues to do so even after a Click calls to public void OnGroundClick.

Disabling then enabling the agent at the start of OnGroundClick does not affect the situation

Though I'm still at a loss I'm at least closer to a solution & there's more info than "this doesn't work, please help!" now.

Here is the Code in it's full context:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.AI;

public class PlayerMovement : MonoBehaviour
{
    public Animator animator;                               //Reference to animator
    public NavMeshAgent agent;                              //Reference to NavMeshAgent
    public float inputHoldDelay = 0.5f;                     //Delay player ability to control character while interacting with interactable object
    public float turnSpeedThreshold = 0.5f;                 //minimum speed before character will turn
    public float speedDampTime = 0.1f;                      //Character rate acceleration
    public float slowingSpeed = 0.175f;                     //Character rate of neg accel
    public float turnSmoothing = 15;                        //Character rotational speed


    private WaitForSeconds inputHoldWait;                   //Coroutine Wait timer to delay player input while interacting with interactable object
    private Vector3 destinationPosition;                    //Player designated destination for the agent to pursue


    private const float stopDistanceProportion = 0.1f;
    private const float navMeshSampleDistance = 4f;


    private readonly int hashSpeedParam = Animator.StringToHash("Speed");


    private void Start()
    {
        Debug.Log(agent.gameObject.name);   //Is the "Player" object
        Debug.Log(agent.isOnNavMesh);       //Evaluates *true*
        agent.updateRotation = false;

        inputHoldWait = new WaitForSeconds(inputHoldDelay);

        destinationPosition = transform.position;
    }


    private void OnAnimatorMove()
    {
        //Velocity = Distance over Time, where Distance = change in position between frames & Time = time between frames
        agent.velocity = animator.deltaPosition / Time.deltaTime;
    }


    private void Update()
    {
        Debug.Log(agent.isOnNavMesh);       //Evaluates *true*
        //If path pending, do nothing
        if (agent.pathPending)
            return;

        float speed = agent.desiredVelocity.magnitude;

        if (agent.remainingDistance <= agent.stoppingDistance * stopDistanceProportion)
        {
            Stopping(out speed);
        }
        else if (agent.remainingDistance <= agent.stoppingDistance)
        {
            Slowing(agent.remainingDistance, out speed);
        }
        else if(speed > turnSpeedThreshold)
        {
            Moving();
        }

        animator.SetFloat(hashSpeedParam, speed, speedDampTime, Time.deltaTime);
    }


    private void Stopping(out float speed)
    {
        agent.isStopped = true;
        transform.position = destinationPosition;
        speed = 0.0f;
    }


    private void Slowing(float distanceToDestination, out float speed)
    {
        agent.isStopped = true;
        transform.position = Vector3.MoveTowards(transform.position, destinationPosition, slowingSpeed * Time.deltaTime);

        float proportionalDistance = 1f - distanceToDestination / agent.stoppingDistance;
        speed = Mathf.Lerp(slowingSpeed, 0, proportionalDistance);
    }


    private void Moving()
    {
        Quaternion targetRotation = Quaternion.LookRotation(agent.desiredVelocity);
        transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, turnSmoothing * Time.deltaTime);
    }


    public void OnGroundClick(BaseEventData data)
    {
        agent.enabled = false;              //Disabling then enabling the agent...
        agent.enabled = true;               //does not change anything.

        Debug.Log(agent.gameObject.name);   //Is the "Player" object
        Debug.Log(agent.isOnNavMesh);       //Evaluates *FALSE*

        PointerEventData pData = (PointerEventData)data;
        NavMeshHit hit;

            //Click World Position, hit info, sample distance, navMesh areas to use
        if (NavMesh.SamplePosition(pData.pointerCurrentRaycast.worldPosition, out hit, navMeshSampleDistance, NavMesh.AllAreas))
        {
            destinationPosition = hit.position;
        }
        else
        {
            destinationPosition = pData.pointerCurrentRaycast.worldPosition;
        }

        //give the agent it's destination
        agent.SetDestination(destinationPosition);
        agent.isStopped = false;
    }
}

解决方案

You have two instances of the PlayerMovement component referring to two different instances of the agent!

One instance is the one agent.isOnNavMesh is true and works properly in Start and Update. The other instance refers to a different agent where agent.isOnNavMesh is false but this is the one whose OnGroundclick is being called.

We can tell because GetInstanceID() returns different values in Update vs. OnGroundClick.

The solution here is to make sure that the OnGroundClick that is registered in the event system is the one in the correct instance of PlayerMovement!

这篇关于Unity3d NavMeshAgent.isOnNavMesh在特定功能中变为假的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆