-Bear in mind I'm fairly inexperienced to Unity and C#-

发生问题的脚本是一个类,用来保存动物AI的所有重要的子 routine 和函数.C#脚本的这一特定部分使动物在停止之前的一段随机时间内对随机位置进行路径查找(这是我能想到的唯一实现随机移动AI的方法,因为它利用了我已经编写的路径查找代码).它的工作方式是在离地面50个单位的随机x,z位置实例化一个空的游戏对象,并将目标位置设置为该游戏对象的位置,然后调用寻路脚本导航到该随机位置. 在FixedUpdate方法中可以找到以下脚本(FixedUpdate与更新相对,因此它的运行频率略低一些),并确保不会在每一帧发生随机移动.

int randNum = UnityEngine.Random.Range(1, 100000);
if (randNum <= 10)
{
    StartCoroutine(DelayForRandomMovement());
}

我面临的问题是,Random.Range调用中的最大值randNumhasrandNum000,或者DelayForRandomMotion()协程中的单个重要行将不会按预期运行.此协程如下所示:

IEnumerator DelayForRandomMovement()
{
    yield return new WaitForSeconds(UnityEngine.Random.Range(1f, 6f)); //wait 1-6 seconds
    stop = false;

    if (stop == false)
    {
        GameObject randomPosObj = Instantiate(randomMovementPrefab);
        randomPosObj.transform.position = new Vector3(UnityEngine.Random.Range(-240, 240), 50, UnityEngine.Random.Range(-240, 240)); //instantiate empty prefab in random x and z pos within range of island size
        target = randomPosObj.transform; //target = the instantiated prefab

        this.gameObject.GetComponent<Rabbit>().LocateFood();

        yield return new WaitForSeconds(UnityEngine.Random.Range(3f, 6f)); //wait 3-6 seconds
        Destroy(randomPosObj);
    }
}

没有正确执行的代码行是this.gameObject.GetComponent<Rabbit>().LocateFood()行,我不知道为什么.该行从Rabbit类(当前类/脚本的子类)调用LocateFood()子 routine (在本例中用于通过路径查找到某个位置,而不是食物).LocateFood()子函数使用各种其他脚本进行计算,因此我将把它的内容包含在本文的最下面.

randNum小于this.gameObject.GetComponent<Rabbit>().LocateFood()000时,this.gameObject.GetComponent<Rabbit>().LocateFood()仍然会执行,但问题是这些动物根本不会寻路.我之所以知道这一点,是因为LocateFood()SUB中的Debug.Log()命令在应该执行的时候仍然会被执行.然而,当最大值设置为this.gameObject.GetComponent<Rabbit>().LocateFood()000时,动物确实会像预期的那样寻径,并且它工作得很完美.唯一的问题是,这种情况发生得非常罕见,导致许多兔子一动不动.

我不能准确地列举我做了什么来try 和修复它,但我一遍又一遍地摆弄这部分代码,似乎什么都不起作用,所以我不得不恢复到您现在看到的状态.有没有人知道有什么方法可以重写这一点,或者给出为什么会发生这种情况的任何提示?谢谢你的帮助.


EDIT: 以下是LocateFood()的内容:

public override void LocateFood()
{
    PathRequestManager.RequestPath(transform.position, target.position, OnPathFound);
}

我知道调用RequestPath()方法很好,因为文本如预期的那样输出到控制台. node 将被计算,其Gizmo将显示在Unity场景视图中.因此,我将只显示回调子 routine OnPathFound()的内容:

public void OnPathFound(Vector3[] waypoints, bool pathSuccessful)
{
    Debug.Log("Path found");
    if (pathSuccessful)
    {
        path = new Path(waypoints, transform.position, turnDst, stoppingDst); ;
        StopCoroutine("FollowPath");
        StartCoroutine("FollowPath");
    }
}

EDIT 2:个 我将粘贴当前代码以显示现在的情况:

Update():

// checks every frame to see if the timer has reached zero
if (timer <= 0)
{
    if (!moving)
    {
        // if animal is not moving AND the timer has reached zero, determine whether movement happens based off chance
        int randNum = UnityEngine.Random.Range(1, 10000);
        if (randNum <= chance)
        {
            RandomMovement();
        }
    }
    else //if animal is currently moving and timer reached zero
    {
        StopRandomMovement();
    }
    timer = UnityEngine.Random.Range(minInterval, maxInterval); // reset the interval timer
}
else
{
    // otherwise, if the timer is greater than zero, reduce the timer by Time.deltaTime (the time in seconds since the last frame)
    timer -= Time.deltaTime; // timer counts down
}

RandomMovement():

Debug.Log("starting wander");
target = new Vector3(UnityEngine.Random.Range(-boundSize, boundSize), height, UnityEngine.Random.Range(-boundSize, boundSize));
rabbit.LocateFood();

moving = true;

StopRandomMovement():

Debug.Log("stopping wander");
StopCoroutine("FollowPath");

moving = false;

我现在面临的问题是兔子不会寻路. 然而,代码似乎按预期执行:moving布尔值经常被设置为true,几秒钟后被设置为false;正确的Debug.Log()消息也会在正确的时刻显示在控制台中; target Vector 3变量也会在判断器中显示适当的随机位置.This also suggests that methods running several times at once is no longer the problem.

因此,新的Update()代码看起来像预期的那样工作,但兔子只是没有开始寻径,尽管寻径代码仍在运行,如"Path Requred"控制台输出所示(它在寻径算法的最初几个方法之一中执行).我会在不同的寻路方法/功能中再扔一些Debug.Log(),希望能告诉我问题所在.

然而,奇怪的是,如果我在按下按钮时单独运行一次路径查找,它会完美地执行一次,并且在测试更新后的代码时,它确实工作得很好,就像我在上一条 comments 中提到的那样.自从我写了这篇文章以来,我没有对任何寻路代码做任何修改,所以我觉得奇怪的是,它只在某些时候起作用.

推荐答案

首先,FixedUpdate()通常比Update()更频繁地被称为,因为它本质上是物理更新循环,正如它在名称中所说的那样,它以固定的时间间隔发生,而Update()不同于Update(),Update()运行的频率与所显示的帧一样频繁.

默认情况下,Unity会try 以每秒约50次的速度运行固定的更新方法,这意味着在运行速度为25fps的游戏中,每经过一帧,您将有大约2次物理更新.

事实上,这可能是你问题的根源.如果您在固定更新或更新中启动协程,这意味着您将在每一帧中启动一大堆协程.启动新的协程不会阻止任何以前的协程运行! 因此,很可能会有一系列协同程序同时运行,它们运行的逻辑相互冲突,比如每隔一秒钟就设置一个新目标.

您实际上并没有明确指出"不能正确执行的代码行".它不是设定了目标吗?它是在设置它,而不是把动物推向它吗?

此外,如果Rabbit是这一类的子类/扩展类,则拨打this.gameObject.GetComponent<Rabbit>().LocateFood();也没有什么意义. 您应该执行以下任一操作:

  • 覆盖Rabbit类中的此协程以执行特定于兔子的逻辑. 或
  • 如果这一逻辑不适用于所有动物,则将其移至Rabbit类. 或
  • 如果你在这里真的需要它,只需判断这个物体是兔子if (this is Rabbit)还是if (this.GetType() == typeof(Rabbit)).

为了调试这个问题,也许可以将启动协程的概率为10万分之一的代码替换为在按下按钮或更确定/确定的情况下运行协程的代码.

最后,有点无关,但是,您设置了stop = false,然后在下一行选中if (stop == false).这完全是多余的,因为Stop将始终为FALSE,因为您在前面已经将其设置为该行.

编辑:这里有一个提议的系统,通过删除它们,真正简化了事情,并降低了复合协程的风险.我还要提一下,协同 routine 在很大程度上已经被异步/等待模式所取代.

// Having this means you don't have to type UnityEngine.Random.Range(...) everywhere, just Random.Range(...).
using UnityEngine;

public class Animal
{
    // I've created variables for some of the hardcoded values you used.
    // You will need to make these protected or public if you want to access them in your extended classes like the Rabbit class.
    [SerializeField]
    private float chance = 0.0001f; 
    [SerializeField]
    private float minInterval = 1;
    [SerializeField]
    private float maxInterval = 6;
    private float timer;

    [SerializeField]
    private float boundSize = 240f;
    [SerializeField]
    private float height = 50f;

    void Update()
    {
        // Checks every frame to see if the timer has reached zero.
        if (tickTimer <= 0)
        {
            // If it has, determine the chance of the movement happening.
            // I've normalized the value here but, since the chance variable is set to 0.0001 the chance is still the same as before, 1 in 10,000.
            float randNum = Random.Range(0f, 1f);
            if (randNum <= chance)
            {
                RandomMovement()
            }
            // Whether the movement happened or not, reset the interval timer.
            timer = Random.Range(minInterval, maxInterval);

            // This is where you could cancel previous movement if you wanted to.
        }
        else
        {
            // Else if the timer is greater than zero, just reduce the timer by Time.deltaTime (the time, in seconds, since the last frame).
            // If it has been 1 second since the last frame (which would be horrible of course) then this will reduce the timer by that amount.
            // So this will count down in seconds.
            // Remember this is not the same for FixedUpdate() as that has a fixed time interval. We use Time.fixedDeltaTime to get that value.
            timer -= Time.deltaTime();
        }
    }

    void RandomMovement()
    {
        // You previously created a new GameObject every time you called this which is bad practice as this literally creates a new object in RAM each time.
        // Also, you only use the position value of the transform which is already Vector3 so you can skip that whole instantiation process you had.
        // You will need to change 'target' to a Vector3 of course.
        target = new Vector3(Random.Range(-boundSize, boundSize), height, Random.Range(-boundSize, boundSize));
    }
}

如果您希望在动物被激活时设置间隔,则可以将其设置为Start()、Awag()或OnEnable().OnEnable()可能是最好的方法,因为它在对象被激活时调用,而不仅仅是在游戏开始时调用.

Csharp相关问答推荐

如何使用C#和Graph API从Azure Directory获取用户详细信息

在Linq中调用需要limit和offset的方法''''

try 还原包时出错:Dapper已经为System.Data.SQLClient定义了依赖项''''

如何在C#中将对象[*,*]直接转换为字符串[*,*]?

在一个模拟上设置一个方法,该模拟具有一个参数,该参数是一个numc函数表达式

如何注册接口类型,类型<>

可为空的泛型属性

.NET HttpClient、JsonSerializer或误用的Stream中的内存泄漏?

MongoDB.NET-将数据绑定到模型类,但无法读取整数值

从依赖项容器在.NET 8中的Program.cs文件中添加IOC

如何在毛伊岛应用程序中完美地同步视图模型和视图的加载?

HttpClient 415不支持的媒体类型错误

如何防止Visual Studio断点以红色突出显示到整行?

异步任务调用程序集

在使用UserManager时,如何包含与其他实体的关系?

MudBlazor Textfield已禁用,但其验证工作正常

MSI无法将快捷方式添加到启动文件夹

获取应用程序版本信息时出现奇怪信息

HttpClient,上传文件时实现进度

无法对包含字符串的列进行排序.请与实体框架联接