程序世界生成无法处理大地图

Procedural world generation can't handle big maps

所以,我正在使用 c# 在 windws 10 上的 unity 2019 中制作类似 terraria 的游戏,它具有程序生成的 tilemaps 并且我将此脚本附加到网格:

using UnityEngine;
using AccidentalNoise;
using System.Collections.Generic;
using UnityEngine.Tilemaps;
using System;

public class CompileTerrain : MonoBehaviour
{

    public TileBase dirtTile;
    public TileBase grassTile;
    public TileBase stoneTile;

    public List<GameObject> fractalLayers = new List<GameObject>();

    public Tilemap grid;
    public int width;
    public int height;

    public float seed;
    public int caveSmoothness = 2;

    void Start()
    {
        grid.ClearAllTiles();

        int touchCount = 0;
        Vector3Int newPos;
        double nx, ny;

        ModuleBase combinedTerrain = CavesAndMountains((uint)seed);
        List<Vector3Int> terrainCoords = new List<Vector3Int>();
        SMappingRanges ranges = new SMappingRanges();

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                nx = (ranges.mapx0 + ((double)x / (double)width) * (ranges.mapx1 - ranges.mapx0)) * 3;
                ny = (ranges.mapy0 + ((double)y / (double)height) * (ranges.mapy1 - ranges.mapy0)) * 3;

                if (combinedTerrain.Get(nx, ny) > 0f)
                {
                    terrainCoords.Add(new Vector3Int(x, height - y, 0));
                }
            }
        }

        List<Tuple<int, int>> neighbors = new List<Tuple<int, int>>() {Tuple.Create(1, 1), Tuple.Create(-1, -1),
                                                                       Tuple.Create(0, 1), Tuple.Create(1, 0),
                                                                       Tuple.Create(0, -1), Tuple.Create(-1, 0),
                                                                       Tuple.Create(-1, 1), Tuple.Create(1, -1)};

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

                    }

                }

            }
        }

        foreach (Vector3Int blck in terrainCoords)
        {
            grid.SetTile(blck, stoneTile);
        }

        terrainCoords.Sort((x, y) => x.x == y.x ? x.y.CompareTo(y.y) : x.x.CompareTo(y.x));
        terrainCoords.Reverse();

        TileBase selectedTile;
        int depth = 0;
        int lastx = 0;
        int lasty = terrainCoords[0].y + 1;

        foreach (Vector3Int blck in terrainCoords)
        {
            depth = blck.x != lastx ? 0 : depth;
            lasty = blck.x != lastx ? blck.y + 1 : lasty;

            selectedTile = depth < 4 ? grassTile : stoneTile;
            selectedTile = 3 < depth && depth < 30 ? dirtTile : selectedTile;

            grid.SetTile(blck, selectedTile);

            lastx = blck.x;
            depth += lasty - blck.y;
            lasty = blck.y;
        }

        int layerNum = 1;
        List<Vector3Int> posList = new List<Vector3Int>();

        foreach (GameObject layer in fractalLayers)
        {
            GetPerlinLayer component = layer.GetComponent<GetPerlinLayer>();

            for (int k = 0; k < component.populateCount; k++)
            {
                layerNum++;
                foreach (Vector3Int pos in component.GetFractalCoords(width, height, (uint)(seed * layerNum)))
                    if (grid.GetTile(pos) != null && grid.GetTile(pos) != grassTile)
                    {
                        grid.SetTile(pos, component.defaultTile);
                    }
            }
        }
    }

    public static ModuleBase CavesAndMountains(uint seed)
    {
        AccidentalNoise.Gradient ground_gradient = new AccidentalNoise.Gradient(0, 0, 0, 1);

        // lowlands
        Fractal lowland_shape_fractal = new Fractal(FractalType.BILLOW, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 2, 0.25, seed);
        AutoCorrect lowland_autocorrect = new AutoCorrect(lowland_shape_fractal, 0, 1);
        ScaleOffset lowland_scale = new ScaleOffset(0.125, -0.45, lowland_autocorrect);
        ScaleDomain lowland_y_scale = new ScaleDomain(lowland_scale, null, 0);
        TranslatedDomain lowland_terrain = new TranslatedDomain(ground_gradient, null, lowland_y_scale);

        // highlands
        Fractal highland_shape_fractal = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 4, 2, seed);
        AutoCorrect highland_autocorrect = new AutoCorrect(highland_shape_fractal, -1, 1);
        ScaleOffset highland_scale = new ScaleOffset(0.25, 0, highland_autocorrect);
        ScaleDomain highland_y_scale = new ScaleDomain(highland_scale, null, 0);
        TranslatedDomain highland_terrain = new TranslatedDomain(ground_gradient, null, highland_y_scale);

        // mountains
        Fractal mountain_shape_fractal = new Fractal(FractalType.RIDGEDMULTI, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 8, 1, seed);
        AutoCorrect mountain_autocorrect = new AutoCorrect(mountain_shape_fractal, -1, 1);
        ScaleOffset mountain_scale = new ScaleOffset(0.3, 0.15, mountain_autocorrect);
        ScaleDomain mountain_y_scale = new ScaleDomain(mountain_scale, null, 0.15);
        TranslatedDomain mountain_terrain = new TranslatedDomain(ground_gradient, null, mountain_y_scale);

        // terrain
        Fractal terrain_type_fractal = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 3, 0.125, seed);
        AutoCorrect terrain_autocorrect = new AutoCorrect(terrain_type_fractal, 0, 1);
        ScaleDomain terrain_type_y_scale = new ScaleDomain(terrain_autocorrect, null, 0);
        AccidentalNoise.Cache terrain_type_cache = new AccidentalNoise.Cache(terrain_type_y_scale);
        Select highland_mountain_select = new Select(terrain_type_cache, highland_terrain, mountain_terrain, 0.55, 0.2);
        Select highland_lowland_select = new Select(terrain_type_cache, lowland_terrain, highland_mountain_select, 0.25, 0.15);
        AccidentalNoise.Cache highland_lowland_select_cache = new AccidentalNoise.Cache(highland_lowland_select);
        Select ground_select = new Select(highland_lowland_select_cache, 0, 1, 0.5, null);

        // caves
        Fractal cave_shape = new Fractal(FractalType.RIDGEDMULTI, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 1, 4, seed);
        Bias cave_attenuate_bias = new Bias(highland_lowland_select_cache, 0.65);
        Combiner cave_shape_attenuate = new Combiner(CombinerTypes.MULT, cave_shape, cave_attenuate_bias);
        Fractal cave_perturb_fractal = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 6, 3, seed);
        ScaleOffset cave_perturb_scale = new ScaleOffset(0.5, 0, cave_perturb_fractal);
        TranslatedDomain cave_perturb = new TranslatedDomain(cave_shape_attenuate, cave_perturb_scale, null);
        Select cave_select = new Select(cave_perturb, 1, 0, 0.75, 0);

        return new Combiner(CombinerTypes.MULT, cave_select, ground_select) as ModuleBase;
    }
}

我非常慷慨地从那些优秀的人那里借来并在偶然的噪音中进行了修改,我制作了一个空的游戏对象,我将这个脚本附加到:

using UnityEngine;
using AccidentalNoise;
using System.Collections.Generic;
using UnityEngine.Tilemaps;

public class GetPerlinLayer : MonoBehaviour
{

    public TileBase defaultTile;
    public float threshold = 0.5f;
    public int populateCount = 5;

    public List<Vector3Int> GetFractalCoords(int width, int height, uint seed)
    {
        double nx, ny;

        ModuleBase combinedTerrain = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 6, 2, seed);
        List<Vector3Int> fractalCoords = new List<Vector3Int>();
        SMappingRanges ranges = new SMappingRanges();

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                nx = (ranges.mapx0 + ((double)x / (double)width) * (ranges.mapx1 - ranges.mapx0)) * 3;
                ny = (ranges.mapy0 + ((double)y / (double)height) * (ranges.mapy1 - ranges.mapy0)) * 3;

                if (combinedTerrain.Get(nx, ny) > threshold)
                {
                    fractalCoords.Add(new Vector3Int(x, height - y, 0));
                }
            }
        }

        return fractalCoords;
    }
}

我为每个游戏对象附加了不同颜色的方形精灵,并将它们保存为预制件。一旦我有了那个预制件,我就把它附加到我以前的脚本中的 fractalLayers 列表来生成矿石。虽然它 运行 在较小的规模上很好,但我不能 运行 在更大的规模上。由于没有使代码 运行 更快的万能药(除了重构,我不知道该怎么做),而且我可能已经使我的部分代码更有效率,因为我是新手,我真的很想从专业人士的角度了解如何使我的代码 运行 更好。我知道我没有解释关于我的项目的所有内容,但它实际上只是一个简单的项目,那些是唯一的脚本和关于它的独特部分,你可以推断我做了什么并填空。任何帮助表示赞赏。如果你能给我额外的推动以及关于这个主题的一些信息,我很乐意推荐一些视频以及你的见解来指导我完成这个过程,因为我更像是一个视觉学习者。谢谢! =)

(For reference, it took me about 4 minutes to build this, with the settings shown here.)

显然我的单词生成方法很好,只是我用来平滑地形的循环花费了太长时间。