Minecraft加载区块的速度

5

我正在开发一个体素地形生成器,一切都很好,我有生物群系、方块等。

令我困扰的是我的Unity项目的速度。如果我在主线程上运行所有内容,只能加载和渲染1到2个区块,而且fps不能低于70。这主要是因为每个区块中的每个方块必须检查它们的邻居以定义其方块边缘的可见性。一个方块有6个邻居,一个区块有16个方块。这会产生大量的检查。

我读过《Minecraft》是单线程的,但我很难相信,因为它的区块加载速度非常快,而且没有fps下降。

我的解决方案是在另一个线程上运行一个区块的方块邻居的检查。这将极大地提高我的fps和我的区块加载速度。但这是正确的方式吗?我不想使用线程,因为我的代码没有被优化。这就像把灰尘藏在地毯下。

感谢阅读

编辑:检查邻居的代码如下:

//Block provides its mesh information
//Check for solidity of adjacent blocks
public virtual MeshData CreateBlockData(Chunk chunk, int x, int y, int z, MeshData meshData)
{
    //Set this to true to turn on collider creation shaped like the chunks
    meshData.useRenderDataForCol = true;

    if (!chunk.GetBlock(x, y + 1, z).IsSolid(Direction.down))
    {
        meshData = FaceDataUp(chunk, x, y, z, meshData);
    }

    if (!chunk.GetBlock(x, y - 1, z).IsSolid(Direction.up))
    {
        meshData = FaceDataDown(chunk, x, y, z, meshData);
    }

    if (!chunk.GetBlock(x, y, z + 1).IsSolid(Direction.south))
    {
        meshData = FaceDataNorth(chunk, x, y, z, meshData);
    }

    if (!chunk.GetBlock(x, y, z - 1).IsSolid(Direction.north))
    {
        meshData = FaceDataSouth(chunk, x, y, z, meshData);
    }

    if (!chunk.GetBlock(x + 1, y, z).IsSolid(Direction.west))
    {
        meshData = FaceDataEast(chunk, x, y, z, meshData);
    }

    if (!chunk.GetBlock(x - 1, y, z).IsSolid(Direction.east))
    {
        meshData = FaceDataWest(chunk, x, y, z, meshData);
    }

    return meshData;
}


//The center of block is the origin
protected virtual MeshData FaceDataUp(Chunk chunk, int x, int y, int z, MeshData meshData)
{
    meshData.AddVertex(new Vector3(x - 0.5f, y + 0.5f, z + 0.5f));
    meshData.AddVertex(new Vector3(x + 0.5f, y + 0.5f, z + 0.5f));
    meshData.AddVertex(new Vector3(x + 0.5f, y + 0.5f, z - 0.5f));
    meshData.AddVertex(new Vector3(x - 0.5f, y + 0.5f, z - 0.5f));
    meshData.AddQuadTriangles();
    //Adds UVs range (0 to 3) to uv list
    meshData.uv.AddRange(FaceUVs(Direction.up));
    return meshData;
}

因此,每个16x16x16块都有4096个方块可以运行此功能。
创建块的代码只是一个包含以下内容的三重循环:
static void GeneratePlainBiome(Chunk chunk, int x, int y, int z, FastNoise noise)
{
    int stoneHeight = GetNoise2D(noise, x, z, 0, 50);
    int chunkX = (int)chunk.transform.position.x;
    int chunkY = (int)chunk.transform.position.y;
    int chunkZ = (int)chunk.transform.position.z;

    if(y == 0)
    {
        chunk.SetBlock(x - chunkX, y - chunkY, z - chunkZ, new BlockSnow());
    }
    else if(stoneHeight > y)
    {
        chunk.SetBlock(x - chunkX, y - chunkY, z - chunkZ, new BlockEarth());
    }
    else if(stoneHeight == y)
    {
        chunk.SetBlock(x - chunkX, y - chunkY, z - chunkZ, new BlockGrass());
    }
    else
    {
        chunk.SetBlock(x - chunkX, y - chunkY, z - chunkZ, new BlockAir());
    }
}

当我填满一个区块后,我会使用以下函数呈现网格:

//Sends the calculated mesh information to the mesh and collision components
void RenderMesh(MeshData meshData)
{
    //Mesh construction
    filter.mesh.Clear();
    filter.mesh.vertices = meshData.vertices.ToArray();
    filter.mesh.triangles = meshData.triangles.ToArray();

    //Uv mapping
    filter.mesh.uv = meshData.uv.ToArray();
    filter.mesh.RecalculateNormals();

    //Collision component creation
    coll.sharedMesh = null;
    Mesh meshColl = new Mesh();
    meshColl.vertices = meshData.colVertices.ToArray();
    meshColl.triangles = meshData.colTriangles.ToArray();
    meshColl.RecalculateNormals();

    coll.sharedMesh = meshColl;
}

所以,总的来说,我正在检查一个区块的16x16x16个块,以了解如何根据相邻的块渲染区块网格。完成该函数后,我可以选择渲染该区块。我正在为玩家周围的一组16x16x16个区块进行操作。(即使我每帧只处理一个区块,我的帧率也会大幅下降。)
编辑2:
对于来自区块脚本的chunk.SetBlock()和chunk.GetBlock():
public void SetBlock(int x, int y, int z, Block block)
{
    if (InRange(x) && InRange(y) && InRange(z))
    {
        blocks[x, y, z] = block;
    }
    else
    {
        LoadBiomes.SetBlock((int)transform.position.x + x, (int)transform.position.y + y, (int)transform.position.z + z, block);
    }
}


public Block GetBlock(int x, int y, int z)
{
    if(InRange(x) && InRange(y) && InRange(z))
    {
        Block block = blocks[x, y, z];

        return block;
    }
    else
    {
        //return new BlockAir();

        int xPos = (int)transform.position.x + x;
        int yPos = (int)transform.position.y + y;
        int zPos = (int)transform.position.z + z;
        Block blockToReturn = LoadBiomes.GetBlock(xPos,yPos,zPos); 

        return blockToReturn;
    }

}

//This work since the values passed to the function are block position - chunk position
public static bool InRange(int index)
{
    if (index < 0 || index >= CHUNK_SIZE)
        return false;

    return true;
}

在块脚本中,isSolid指的是方块是否为实体(如果游戏仅有立方体则并不重要)。
//Every face is solid for a cube
public virtual bool IsSolid(Direction direction)
{
    switch (direction)
    {
        case Direction.north:
            return true;
        case Direction.east:
            return true;
        case Direction.south:
            return true;
        case Direction.west:
            return true;
        case Direction.up:
            return true;
        case Direction.down:
            return true;
    }
    return false;
}

并且来自分析器的图像(不确定是否是所要求的)

Profiler


1
我有一个围绕着玩家的块队列(假设为8x8x8),在渲染块网格之前,我会检查每个块的可见性。@FiringSquadWitness - Pouissante
你在调试版或发布版中测试了性能吗? - user743414
我认为即使我使用了一个发布版本编译也不会有任何帮助。也许我会获得一些fps,但那并不是真正的重点。 - Pouissante
你能提供一下 chunk.GetBlock、.SetBlock 和 .IsSolid 的代码吗?很难说你的性能下降是从哪里来的。(运行分析器真的会有所帮助!) - Immorality
抱歉回答晚了,我尽力提供了你所要求的一切内容。感谢@Immorality的帮助。 - Pouissante
显示剩余8条评论
1个回答

1
我不是专家,但据我所知,Unity 3D 使用多边形而不是体素引擎。体素引擎与之不同。
这种差异的直接结果是,多边形可以高效地表示具有大量空白或均匀填充空间的简单三维结构,而体素则擅长表示非均匀填充的定期采样空间。

https://en.wikipedia.org/wiki/Voxel

详见技术细节:

一些体素引擎所做的是使用大型数组,然后使用它们来确定视野中有什么和没有什么。这与经典的3D多边形做事方式非常不同,该方式始于《毁灭战士》。著名的体素游戏包括“卡曼契”系列、“流亡者”...以及现在的“我的世界”。

1
虽然《我的世界》是最知名的体素游戏,但其性能非常差,可以说是最差的。Unity3d为模仿《我的世界》提供了足够的动力(实际上有人在YouTube上使用Unity3D编写了《我的世界》),我只是在看一些其他多人在线体素项目,看看是否可以在更少的资源消耗下实现。 - eocron
我不了解Minecraft的任何细节,我猜想在2019年,由于硬件加速,你可以通过暴力破解的方式使用多边形来实现体素引擎。 - Christophe Roussy
谢谢您的回答,我不认为这是与Unity相关的问题。我认为我只是有太多的迭代,正如您在我所做的编辑中所看到的那样。此外,我正在渲染玩家旁边的内容。并不是我一次渲染太多,而是我有太多的迭代,我不知道该如何减少它们。 - Pouissante
有趣的是,我听到每个人都说《我的世界》性能不好,但我找不到一篇比它更好或接近其性能的教程。@eocron - Pouissante

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接