Unity 代码带有罕见的空引用,我无法弄清楚

发布于 2025-01-20 08:58:26 字数 1455 浏览 5 评论 0原文

我的 Unity 游戏代码中有一个简单的函数,我正在寻找一个“工作点”——一个供 NPC 执行某些任务的空地方。

这段代码在 99.999% 的情况下都工作得很好,但偶尔会抛出 NULL REFERENCE EXCEPTION,而且我一辈子都无法弄清楚在哪里。我试图抓住所有可能未分配某些内容但找不到它的地方。肯定有一些我遗漏的边缘情况,或者我的代码中的某些内容在某些奇怪的情况下行为不当。

所以我的代码有缺陷,我不明白在哪里。帮我改进一下:

    public Transform[] workSpots;


    public Transform findAvailableWorkSpot() {
        if (workSpots == null || workSpots.Length == 0) return null; // sometimes we have no work spots
        int i=0;
        while (i<10) {
            Transform spot = workSpots[Random.Range(0, workSpots.Length)];
            if (spot != null && spot.childCount == 0) {
                return spot;
            }
            i++;
        }
        // couldn't find one randomly, let's just iterate over them:
        foreach (Transform spot in workSpots) {
            if (spot != null && spot.childCount == 0) {
                return spot;
            }
        }

        Debug.LogError("could not find free workspot on "+gameObject.name);
        return null;
    }

逻辑:

  • 首先,尝试最多10次以获得免费的随机工作点。当 NPC 在那里工作时,我将其作为工作地点的父级,因此如果该地点目前没有 NPC,则 spot.childCount == 0 为 true。
  • 如果失败,那么我只需迭代所有这些并选择第一个免费的。
  • 可以在这里返回 null,调用代码将处理该问题

诊断后端告诉我每天有人在此函数中遇到 2-4 次空引用异常,但诊断不会告诉我行号,我已经本地没见过。我看了一遍又一遍,却找不到它可能在哪里。也许我的代码有更根本的问题?


附加信息:

  • workspots 在 Awake() 中初始化,并且永远不会再次更改,因此我确信函数开头的测试有效,并且在函数运行时它不可能变为 null。

I have a simple function in my Unity game code where I am looking for a "work spot" - an empty place for my NPCs to do some task.

This code works perfectly well 99.999% of the time, but throws a NULL REFERENCE EXCEPTION very occasionally and I can't for the life of me figure out where. I have tried to catch every possible place where something could be unassigned and can't find it. There must be some edge case I am missing, or something in my code that misbehaves under some strange circumstances.

So my code is flawed and I don't understand where. Help me improve it:

    public Transform[] workSpots;


    public Transform findAvailableWorkSpot() {
        if (workSpots == null || workSpots.Length == 0) return null; // sometimes we have no work spots
        int i=0;
        while (i<10) {
            Transform spot = workSpots[Random.Range(0, workSpots.Length)];
            if (spot != null && spot.childCount == 0) {
                return spot;
            }
            i++;
        }
        // couldn't find one randomly, let's just iterate over them:
        foreach (Transform spot in workSpots) {
            if (spot != null && spot.childCount == 0) {
                return spot;
            }
        }

        Debug.LogError("could not find free workspot on "+gameObject.name);
        return null;
    }

Logic:

  • first, try up to 10 times to get a free random work spot. I parent the NPC to the work spot when he is working there, so spot.childCount == 0 is true if the spot has no NPC on it right now.
  • if that fails, then I just iterate over all of them and pick the first free one.
  • it is ok to return null here, the calling code will handle that

The Diagnostic backend tells me that 2-4 times a day someone experiences a Null Reference Exception in this function, but diagnostics doesn't tell me the line number and I've never seen it locally. I'm looking at it again and again and I can't spot where it could be. Maybe something more fundamental is wrong with my code?


Additional Information:

  • workspots is initialized in Awake() and is never again changed, so I am sure that the test at the beginning of the function works and it's not possible that it goes to null while the function is running.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

め可乐爱微笑 2025-01-27 08:58:26

这不是直接对例外的来源的答案。

但是,如果您愿意使用LINQ,则可以简单地将代码提供很多!

我只需使用

using System.Linq;

...

public Transform[] workSpots;

public bool TryFindAvailableWorkSpot(out Transform freeSpot)
{
    // First heck this component even is alive
    if (!this || workSpots == null || workSpots.Length == 0)
    {
        freeSpot = null;
        return false;
    }

    // First "Where" filters out only those spots that exist and have no children
    // then "OrderBy" randomizes these results
    // then we take the first one or null if there isn't any
    freeSpot = workSpots.Where(spot => spot && spot.childCount == 0).OrderBy(spot => Random.value).FirstOrDefault();

    // use the implicit bool operator for checking if we have found a valid reference
    return freeSpot;
}

,然后就不用使用

var spot = yourClass.findAvailableWorkSpot();

,不得不再次检查null和潜在的异常,您宁愿简单地在同一行中使用EG进行检查

if(yourClass.TryFindAvailableWorkSpot(out var spot))
{
    ... Do someting with spot
}
else
{
    Debug.LogError("could not find free workspot");
}

<

  • a href =“ https:// .microsoft.com/dotnet/api/system.linq.enumoser.where“ rel =“ nofollow noreferrer”> linq 其中
  • linq orderby
  • andy.value
  • ​“ nofollow noreferrer”> unityEngine.Object隐式bool操作员

This is not directly an answer to where exactly the exception comes from.

But if you are open to using Linq you can simplyfy your code a lot!

I would simply use

using System.Linq;

...

public Transform[] workSpots;

public bool TryFindAvailableWorkSpot(out Transform freeSpot)
{
    // First heck this component even is alive
    if (!this || workSpots == null || workSpots.Length == 0)
    {
        freeSpot = null;
        return false;
    }

    // First "Where" filters out only those spots that exist and have no children
    // then "OrderBy" randomizes these results
    // then we take the first one or null if there isn't any
    freeSpot = workSpots.Where(spot => spot && spot.childCount == 0).OrderBy(spot => Random.value).FirstOrDefault();

    // use the implicit bool operator for checking if we have found a valid reference
    return freeSpot;
}

and then instead of using

var spot = yourClass.findAvailableWorkSpot();

and having to again check for null and potential exceptions you would rather simply do the check in the same line using e.g.

if(yourClass.TryFindAvailableWorkSpot(out var spot))
{
    ... Do someting with spot
}
else
{
    Debug.LogError("could not find free workspot");
}

See

稳稳的幸福 2025-01-27 08:58:26

我认为您没有看到异常发生的正确位置。根据您编写代码的方式,workSpots不可能为 null 或为空。

我唯一能看到发生空引用异常的地方是当您尝试访问 workSpots[index] ->返回一个为 null 的 Transform 对象,您尝试使用它(但在您的代码中您已为此做好准备)。
也许您使用该特定方法的地方在尝试访问它之前不会检查您返回的 Transform 是否为 null。
如果您的代码写得很好并且您不相信它,那么空引用异常可能是在尝试记录错误时由 gameObject.name 引起的。如果不是那样的话,老实说我不知道​​。

I don't think you are looking at the right place where the exception happened. The way you've written the code it's just not possible for workSpots to be null or empty.

The only places where I can see a null reference exception happening is if you are trying to access workSpots[index] -> and that returns a Transform object that is null and you try using it (but in your code you are prepared for that).
Maybe the places where you use that particular method do not check if the Transform that you are returning is null before trying to access it.
If your code is well written and you don't believe it then the null reference exception is probably caused by gameObject.name when trying to log an error. If it isn't that then I have no idea honestly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文