提问人:Tom 提问时间:4/8/2022 最后编辑:Tom 更新时间:4/8/2022 访问量:852
Unity 代码具有我无法弄清楚的罕见的空引用
Unity code with rare null reference that I can't figure out
问:
我的 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
- 如果失败了,那么我只是遍历所有这些并选择第一个免费的。
- 可以在这里返回 null,调用代码将处理它
诊断后端告诉我,每天有 2-4 次有人在此函数中遇到 Null 引用异常,但诊断没有告诉我行号,而且我从未在本地看到过。我一遍又一遍地看着它,但我无法发现它可能在哪里。也许我的代码有更根本的问题?
其他信息:
- workspots 在 Awake() 中初始化并且永远不会再次更改,因此我确信函数开头的测试可以正常工作,并且在函数运行时它不可能变为 null。
答:
我不认为你正在寻找发生异常的正确位置。你编写代码的方式不可能是空或空的。workSpots
我唯一可以看到发生空引用异常的地方是,如果您尝试访问 -> 并且返回一个 null 的 Transform 对象,并且您尝试使用它(但在您的代码中,您为此做好了准备)。
也许您使用该特定方法的地方在尝试访问它之前不会检查您返回的 Transform 是否为 null。
如果你的代码写得很好,但你不相信它,那么空引用异常可能是由于尝试记录错误时引起的。如果不是这样,那么老实说我不知道。workSpots[index]
gameObject.name
评论
这并不是对异常确切来源的直接回答。
但是,如果您愿意使用 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
if(yourClass.TryFindAvailableWorkSpot(out var spot))
{
... Do someting with spot
}
else
{
Debug.LogError("could not find free workspot");
}
看
评论
Transform[] workSpots;
workSpots[Random.Range(0, workSpots.Length)]
workSpots.Length
NullReferenceException
IndexOutOfRangeException
Random.Range
Random.Range(0, workSpots.Length)
workSpots.Length
workSpots
Component.gameObject
可以“等效于”,即在此对象被销毁的帧中调用此方法。但在这种情况下,对自己的调用应该已经抛出......除非它来自同一个班级而不是来自外部。虽然不确定..我认为这种情况宁愿抛出 Unity 的自定义案例null
findAvailableWorkSpot
MissingReferenceException