Unity C# 中意外调用方法

问题描述 投票:0回答:1

我正在对一个学习项目进行优化,在该项目中我将重新创建 Minecraft 的基本功能。

我已经成功实现了块的边界和角立方体的优化。当玩家触发块生成过程时,所有边都完全覆盖的边界和角上的立方体要么被禁用(如果它们已经存在),要么不会被实例化(如果它们是新块的新立方体)。

为了使代码更清晰,我决定将边框和角立方体优化移动到单独的类中,每个类都继承自 MapOptimization 脚本。我从 BorderOptimization 脚本开始,并使用 onIsBorderCube 操作来触发 BorderCubeOptimizationSequence。我已在 BorderOptimizationStart() 方法中订阅了 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);
        }
    }
}

BorderOptimization 脚本如下⬇️

脚本不完整,我删除了其他内容以使其预览简短。如果您错过任何内容,请告诉我,我会添加它。

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 方法,而这是不应该的。

我已采取的步骤:

  1. 我恢复到以前的实现,在该版本中,不会发生对 ProcessAllCubeDataOfUpcommingChunk 的额外调用。这表明问题可能不在于 onIsBorderCube 操作本身,而在于代码的其他地方。

  2. 我已经审查了对 ProcessAllCubeDataOfUpcommingChunk 的所有引用,并确认它只能在相应的脚本中调用一次。

  3. 在调试时,我注意到当额外调用该方法时,调试器不会返回到调用 ProcessAllCubeDataOfUpcommingChunk 的位置。

  4. 我还检查了调用堆栈中此方法调用的有效和无效迭代。奇怪的是,两种情况下的调用堆栈看起来都是相同的,尽管其中一种情况会导致额外的调用。

有效迭代 在此输入图片描述

无效迭代 在此输入图片描述

我想这可能是由于事件函数的执行顺序而发生的,但很难找到原因。

我正在发送一个视频我调试问题时的样子。

c# visual-studio unity-game-engine game-development
1个回答
0
投票

问题出在我对继承如何运作的误解。

如果我将Base scriptDerived script放在同一个游戏对象上,那么将创建两个Base script实例,并且ProcessAllCubeDataOfUpcomingChunk将被调用两次。

© www.soinside.com 2019 - 2024. All rights reserved.