Как упростить интенсивность использования процессора для L oop C# - PullRequest
3 голосов
/ 10 марта 2020

Итак, я создаю процедурно сгенерированный мир в C#, Visual Studio на Windows 10 в Unity. У меня была проблема с выяснением, почему моя карта не может обрабатывать большие измерения, пока я не наткнулся на этот набор циклов в своем коде.

    for (int index = 0; index < terrainCoords.Count; index++)
        {
            if (index == terrainCoords.Count)
            {
                break;
            }

            touchCount = 0;

            for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
            {
                newPos = new Vector3Int(terrainCoords[index].x + neighbors[posAdd].Item1, terrainCoords[index].y + neighbors[posAdd].Item2, 0);
                touchCount += terrainCoords.Contains(newPos) ? 1 : 0;
            }

            if (touchCount < 2)
            {
                terrainCoords.Remove(terrainCoords[index]);
            }

        }

    for (int j = 0; j < caveSmoothness; j++)
    {
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                if (!terrainCoords.Contains(new Vector3Int(x, y, 0)))
                {
                    touchCount = 0;

                    for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
                    {
                        newPos = new Vector3Int(x + neighbors[posAdd].Item1, y + neighbors[posAdd].Item2, 0);
                        touchCount += terrainCoords.Contains(newPos) ? 1 : -1;
                    }

                    if (touchCount > 1)
                    {
                            terrainCoords.Add(new Vector3Int(x, y, 0));
                    }

                }

            }

        }
    }

Теперь, для справки, terrainCoords - это список Vector3Int который я предварительно сгенерировал, чтобы сделать карту, хотя получающаяся карта грязная, поэтому эти циклы for очищают их, просматривая список кортежей, называемых «соседями», который представляет собой набор чисел, которые при добавлении к координатам блок дает все координаты блоков, непосредственно прикасаясь к ним. Цель первого l oop состоит в том, чтобы удалить свободно плавающие блоки solid, которые недостаточно касаются блоков solid, чтобы составить какую-либо важную структуру. Второй l oop - заполнить случайные карманы, которые могут появиться, добавив блоки в позиции пустых блоков, которые не касаются достаточно пустых блоков. И когда caveSmoothness увеличивается, он также может заполнять небольшие узкие туннели. Тем не менее, если бы у меня была карта, скажем, с размером 100 * 100 и caveSmoothness, равным 2, ей пришлось бы перебирать себя 100 000 000 раз, что слишком много. Это очень важный процесс, но не нужно его приводить к неудобным картам. Чтобы предварительно сгенерировать ландшафт, я использую пример мира Minecraft от Accidental Noise со следующими настройками:

terraintree=
{
    {name="ground_gradient",               type="gradient",         x1=0, x2=0, y1=0, y2=1},

    {name="lowland_shape_fractal",         type="fractal",          fractaltype=anl.BILLOW, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=2, frequency=0.25},
    {name="lowland_autocorrect",           type="autocorrect",      source="lowland_shape_fractal", low=0, high=1},
    {name="lowland_scale",                 type="scaleoffset",      source="lowland_autocorrect", scale=0.125, offset=-0.45},
    {name="lowland_y_scale",               type="scaledomain",      source="lowland_scale", scaley=0},
    {name="lowland_terrain",               type="translatedomain",  source="ground_gradient", ty="lowland_y_scale"},

    {name="highland_shape_fractal",        type="fractal",          fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=4, frequency=2},
    {name="highland_autocorrect",          type="autocorrect",      source="highland_shape_fractal", low=-1, high=1},
    {name="highland_scale",                type="scaleoffset",      source="highland_autocorrect", scale=0.25, offset=0},
    {name="highland_y_scale",              type="scaledomain",      source="highland_scale", scaley=0},
    {name="highland_terrain",              type="translatedomain",  source="ground_gradient", ty="highland_y_scale"},

    {name="mountain_shape_fractal",        type="fractal",          fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=8, frequency=1},
    {name="mountain_autocorrect",          type="autocorrect",      source="mountain_shape_fractal", low=-1, high=1},
    {name="mountain_scale",                type="scaleoffset",      source="mountain_autocorrect", scale=0.45, offset=0.15},
    {name="mountain_y_scale",              type="scaledomain",      source="mountain_scale", scaley=0.25},
    {name="mountain_terrain",              type="translatedomain",  source="ground_gradient", ty="mountain_y_scale"},

    {name="terrain_type_fractal",          type="fractal",          fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=3, frequency=0.125},
    {name="terrain_autocorrect",           type="autocorrect",      source="terrain_type_fractal", low=0, high=1},
    {name="terrain_type_y_scale",          type="scaledomain",      source="terrain_autocorrect", scaley=0},
    {name="terrain_type_cache",            type="cache",            source="terrain_type_y_scale"},
    {name="highland_mountain_select",      type="select",           low="highland_terrain", high="mountain_terrain", control="terrain_type_cache", threshold=0.55, falloff=0.2},
    {name="highland_lowland_select",       type="select",           low="lowland_terrain", high="highland_mountain_select", control="terrain_type_cache", threshold=0.25, falloff=0.15},
    {name="highland_lowland_select_cache", type="cache",            source="highland_lowland_select"},
    {name="ground_select",                 type="select",           low=0, high=1, threshold=0.5, control="highland_lowland_select_cache"},

    {name="cave_shape",                    type="fractal",           fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
    {name="cave_attenuate_bias",           type="bias",              source="highland_lowland_select_cache", bias=0.45},
    {name="cave_shape_attenuate",          type="combiner",          operation=anl.MULT, source_0="cave_shape", source_1="cave_attenuate_bias"},
    {name="cave_perturb_fractal",          type="fractal",           fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=6, frequency=3},
    {name="cave_perturb_scale",            type="scaleoffset",       source="cave_perturb_fractal", scale=0.5, offset=0},
    {name="cave_perturb",                  type="translatedomain",   source="cave_shape_attenuate", tx="cave_perturb_scale"},
    {name="cave_select",                   type="select",            low=1, high=0, control="cave_perturb", threshold=0.48, falloff=0},

    {name="ground_cave_multiply",          type="combiner",          operation=anl.MULT, source_0="cave_select", source_1="ground_select"}
}

И, несмотря на мои усилия найти настройку шумоподавления / фрактальной плавности в их документах и ​​настроить их, Кажется, я не могу получить те же результаты, что и на машине Рут-Голдберга, как мои циклы for. Если вы можете упростить цикл for, найти более эффективный способ достижения этих результатов или изменить настройки фрактала так, как я не заметил, ваш вклад будет очень важен. Благодарю вас! =)

Ответы [ 2 ]

2 голосов
/ 10 марта 2020

Мне кажется, это слишком сложно. Это 2D массив, так почему бы не начать с простого 2D массива?

Random rnd = new Random(1);
int xSize = 10000;
int ySize = 1000;
byte[,] terrainCoords = new byte[ySize + 2, xSize + 2];

for (int y = 1; y <= ySize; ++y)
{
    for (int x = 1; x <= xSize; ++x)
    {
        if (rnd.Next(100) < 40) //40% fill ratio
        {
            terrainCoords[y, x] = 1;
        }
    }
}

var st = new System.Diagnostics.Stopwatch();
st.Start();

for (int y = 1; y <= ySize; ++y)
{
    for (int x = 1; x <= xSize; ++x)
    {
        int touchCount =
              terrainCoords[y - 1, x - 1]
            + terrainCoords[y - 1, x]
            + terrainCoords[y - 1, x + 1]
            + terrainCoords[y, x - 1]
            + terrainCoords[y, x + 1]
            + terrainCoords[y + 1, x - 1]
            + terrainCoords[y + 1, x]
            + terrainCoords[y + 1, x + 1];

        if (touchCount < 2)
        {
            terrainCoords[y, x] = 0;
        }
    }
}           

int caveSmoothness = 5;
for (int j = 0; j < caveSmoothness; j++)
{
    for (int y = 1; y <= ySize; ++y)
    {
        for (int x = 1; x <= xSize; ++x)
        {
            int touchCount =
               terrainCoords[y - 1, x - 1]
             + terrainCoords[y - 1, x]
             + terrainCoords[y - 1, x + 1]
             + terrainCoords[y, x - 1]
             + terrainCoords[y, x + 1]
             + terrainCoords[y + 1, x - 1]
             + terrainCoords[y + 1, x]
             + terrainCoords[y + 1, x + 1];

            if (touchCount > 4)
            {
                terrainCoords[y, x] = 1;
            }
        }
    }
}

st.Stop();
Console.WriteLine(st.ElapsedMilliseconds.ToString()); //800ms

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

1 голос
/ 10 марта 2020

Одна из выдающихся оптимизаций заключается в коротком замыкании l oop для удаления terrainCoords элементов. Вам не важен истинный счет, только то, что он <2, поэтому в тот момент, когда <code>touchCount не пройдёт этот тест, вы получите залог от остальной части l oop.

for (int posAdd = 0; posAdd < neighbors.Count && touchCount < 2; posAdd++)
{
    newPos = new Vector3Int(terrainCoords[index].x + neighbors[posAdd].Item1, terrainCoords[index].y + neighbors[posAdd].Item2, 0);
    touchCount += terrainCoords.Contains(newPos) ? 1 : 0;
}

if (touchCount < 2)
{
    terrainCoords.Remove(terrainCoords[index]);
}

. Вы можете применить похожее короткое замыкание на внутреннюю часть l oop для гладкости пещеры.

...