我正在对一个学习项目进行优化,在该项目中我将重新创建 Minecraft 的基本功能。
我已经成功实现了块的边界和角立方体的优化。当玩家触发块生成过程时,所有边都完全覆盖的边界和角上的立方体要么被禁用(如果它们已经存在),要么不会被实例化(如果它们是新块的新立方体)。
为了使代码更清晰,我决定将边框和角立方体优化移动到单独的类中,每个类都继承自 MapOptimization 脚本。我从 BorderOptimization 脚本开始,并使用 onIsBorderCube 操作来触发 BorderCubeOptimizationSequence。我已在 BorderOptimization 的 Start() 方法中订阅了 onIsBorderCube 操作。虽然正确调用了该操作,但在完成每个有效迭代后我遇到了 NullReferenceException。
异常发生在 ProcessAllCubeDataOfUpcomingChunk 方法完成后。由于某种原因,再次调用 ProcessAllCubeDataOfUpcomingChunk 方法,但这次 onIsBorderCube 操作为 null,导致崩溃。
脚本不完整,我删除了其他内容以保持预览简短。如果您错过任何内容,请告诉我,我会添加它。
public class MapOptimization : MonoBehaviour
{
protected Action<Dictionary<Vector3, CubeData>, CubeData, Border> onIsBorderCube;
private void ProcessAllCubeDataOfUpcommingChunk(Dictionary<Vector3, CubeData> newChunkFieldData, Vector3 centerOfNewChunk)
{
centerOfXNegativeNeighbourChunk = new Vector3(centerOfNewChunk.x - mapGenerator.gridSize.x, centerOfNewChunk.y, centerOfNewChunk.z);
centerOfXPositiveNeighbourChunk = new Vector3(centerOfNewChunk.x + mapGenerator.gridSize.x, centerOfNewChunk.y, centerOfNewChunk.z);
centerOfZNegativeNeighbourChunk = new Vector3(centerOfNewChunk.x, centerOfNewChunk.y, centerOfNewChunk.z - mapGenerator.gridSize.z);
centerOfZPositiveNeighbourChunk = new Vector3(centerOfNewChunk.x, centerOfNewChunk.y, centerOfNewChunk.z + mapGenerator.gridSize.z);
XNegativeCorner = centerOfNewChunk.x - Mathf.Ceil((float)mapGenerator.gridSize.x / 2.0f) + 1.0f;
XPositiveCorner = centerOfNewChunk.x + Mathf.Ceil((float)mapGenerator.gridSize.x / 2.0f) - 1.0f;
ZNegativeCorner = centerOfNewChunk.z - Mathf.Ceil((float)mapGenerator.gridSize.x / 2.0f) + 1.0f;
ZPositiveCorner = centerOfNewChunk.z + Mathf.Ceil((float)mapGenerator.gridSize.x / 2.0f) - 1.0f;
foreach (KeyValuePair<Vector3, CubeData> newCubeData in newChunkFieldData)
{
OptimizeDataOfNewChunk(newCubeData.Value, centerOfNewChunk, newChunkFieldData);
}
}
private void OptimizeDataOfNewChunk(CubeData newCubeData, Vector3 centerOfNewChunk, Dictionary<Vector3, CubeData> newChunkFieldData)
{
Border newChunkBorder = Border.Null;
if (isCubeAtBorder(newCubeData.position, ref newChunkBorder))
{
Corner newCubeCorner = Corner.Null;
if (isNewCubeAtCorner(newCubeData, ref newCubeCorner))
{
CornerCubeOptimizationSequence(newCubeData, centerOfNewChunk, newChunkBorder, newCubeCorner);
}
else
{
onIsBorderCube(newChunkFieldData, newCubeData, newChunkBorder);
}
}
else
{
DeactiavateSurroundedCubeData(newCubeData, newChunkFieldData);
}
}
}
脚本不完整,我删除了其他内容以使其预览简短。如果您错过任何内容,请告诉我,我会添加它。
public class BorderOptimization : MapOptimization
{
void Start()
{
Debug.Log("onIsBorder is subscribbed");
onIsBorderCube += BorderCubeOptimizationSequence;
}
private void BorderCubeOptimizationSequence(Dictionary<Vector3, CubeData> newChunkFieldData, CubeData newCubeData, Border newChunkBorder)
{
Border neighbourChunkBorder = Border.Null;
Vector3 neighbourCubePosition = Vector3.zero;
Vector3 neighbourChunkCenter = Vector3.zero;
SetNeighborChunkValues(newChunkBorder, newCubeData.position, ref neighbourChunkBorder, ref neighbourCubePosition, ref neighbourChunkCenter);
if (!DoesNeighborChunkExist(neighbourChunkCenter))
{
return;
}
Dictionary<Vector3, CubeParameters> neighbourChunkField = mapGenerator.dictionaryOfCentersWithItsChunkField[neighbourChunkCenter];
// Return if New Cube in New Chunk isn't surrounded with cubes from each sides
if (!IsBorderCubeSurrounded<CubeData, CubeParameters>(newChunkFieldData, newCubeData.position, neighbourChunkField, neighbourCubePosition, newChunkBorder))
{
return;
}
newCubeData.isCubeDataSurrounded = true;
// Return if Neighbor Cube in Neighbor Chunk isn't surrounded with cubes from each sides
if (!IsBorderCubeSurrounded<CubeParameters, CubeData>(neighbourChunkField, neighbourCubePosition, newChunkFieldData, newCubeData.position, neighbourChunkBorder))
{
return;
}
neighbourChunkField[neighbourCubePosition].cubeInstance.gameObject.SetActive(false);
}
}
我面临的问题不是 onIsBorderCube 操作抛出 NullReferenceException,而是额外调用了 ProcessAllCubeDataOfUpcommingChunk 方法,而这是不应该的。
我恢复到以前的实现,在该版本中,不会发生对 ProcessAllCubeDataOfUpcommingChunk 的额外调用。这表明问题可能不在于 onIsBorderCube 操作本身,而在于代码的其他地方。
我已经审查了对 ProcessAllCubeDataOfUpcommingChunk 的所有引用,并确认它只能在相应的脚本中调用一次。
在调试时,我注意到当额外调用该方法时,调试器不会返回到调用 ProcessAllCubeDataOfUpcommingChunk 的位置。
我还检查了调用堆栈中此方法调用的有效和无效迭代。奇怪的是,两种情况下的调用堆栈看起来都是相同的,尽管其中一种情况会导致额外的调用。
有效迭代 在此输入图片描述
无效迭代 在此输入图片描述
我想这可能是由于事件函数的执行顺序而发生的,但很难找到原因。
我正在发送一个视频我调试问题时的样子。
问题出在我对继承如何运作的误解。
如果我将Base script和Derived script放在同一个游戏对象上,那么将创建两个Base script实例,并且ProcessAllCubeDataOfUpcomingChunk将被调用两次。