Скорость Minecraft для загрузки кусков - PullRequest
5 голосов
/ 15 апреля 2019

Я работаю над воксельным генератором местности.Все хорошо, у меня есть биомы, блоки и т. Д.

Меня щекочет скорость моего проекта в единстве.Если я запускаю все в основном потоке, я могу только загружать и рендерить 1-2 фрагмента, не опускаясь ниже 70 кадров в секунду.Это главным образом потому, что каждый блок в чанке должен проверять своих соседей, чтобы определить видимость на их стороне блока.Блок имеет 6 соседей, а блок имеет 16 блоков.Это делает много проверок очень быстро.

Я читал, что майнкрафт однопоточный, но мне трудно поверить, что, поскольку скорость загрузки его чанка достаточно высокая и без падений 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 блоков для запуска этой функции.

Код, который создает блоки, представляет собой просто тройной цикл 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());
    }
}

После того, как я заполнил блок, я рендерил сеткус помощью этой функции:

//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 () из сценария chunk:

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 Ответ

1 голос
/ 16 апреля 2019

Я не эксперт, но, насколько я знаю, Unity 3D использует полигоны и не является воксельным двигателем. Воксельные двигатели бывают разные.

Прямым следствием этого различия является то, что полигоны могут эффективно представлять простые трехмерные структуры с большим количеством пустых или однородно заполненное пространство, в то время как вокселы превосходно представляют регулярно отбираемые пробелы, которые заполнены неоднородно.

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

Технические подробности см .:

Что делают некоторые воксельные движки, так это используют большие массивы, а затем используют их для определения того, что находится в поле зрения, а что нет. Это сильно отличается от классического трехмерного полигона, который начинался с Quake. Известные воксельные игры включают серию Comanche, Outcast ... и теперь Minecraft.

...