c# - 加载 block 的 Minecraft 速度

标签 c# multithreading unity3d minecraft procedural-generation

我正在研究体素地形生成器。一切都很好,我有生物群落、方 block 等。

令我兴奋的是我的项目在 unity 中的速度。如果我在主线程上运行所有内容,我只能加载和渲染 1 到 2 个 block ,而不会低于 70fps。这主要是因为 block 中的每个 block 都必须检查它们的邻居来定义它们的 block 侧可见性。一个 block 有 6 个邻居,一个 block 有 16 个 block 。这样可以非常快速地进行大量检查。

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

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

感谢阅读

编辑:检查邻居的代码

//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 block 的 block 都有 4096 个 block 来运行此函数。

创建 block 的代码只是一个包含以下内容的三重 for 循环:

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());
    }
}

填充 block 后,我使用此函数渲染网格:

//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;
}

因此,为了继续,我正在检查 block 的 16x16x16 block ,以了解如何根据邻居渲染 block 网格。完成该功能后,我可以选择渲染 block 。我正在这样做,假设玩家周围有一个 16x16x16 的 block 。 (即使我每帧做一个 block ,我的 fps 下降也很糟糕。)

编辑 2:

对于 block 脚本中的 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;
}

block 脚本中的 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

最佳答案

我不是专家,但据我所知,unity 3d 使用多边形而不是体素引擎。体素引擎不同。

A direct consequence of this difference is that polygons can efficiently represent simple 3D structures with lots of empty or homogeneously filled space, while voxels excel at representing regularly sampled spaces that are non-homogeneously filled.

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

技术细节见:

一些体素引擎所做的是使用大数组,然后使用它们来确定视野中的内容,以及不在视野中的内容。这与从 Quake 开始的经典 3d 多边形做事方式非常不同。著名的体素游戏包括科曼奇系列、流浪者……以及现在的我的世界。

关于c# - 加载 block 的 Minecraft 速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55681161/

相关文章:

javascript - 将图像从 SQL Server 插入到 Word 文档文件

c# - 可选参数后跟 Params

c++ - 为 win32 线程使用不同的静态库内存

iOS 不加载下一级

c# - 监听 8080 端口的 WebSocket Windows 服务

c# - 向 RDLC 报告添加新数据集时 Visual Studio 挂起

c# - 线程安全的公共(public)字符串

android - 使用处理程序发布到 UI 线程

c# - UWP App 停止接收 UDP 多播消息

c# - 如何在运行时使用 .bmp 文件并在 Unity 中创建纹理?