Процедурная генерация карты - определите соседние тайлы

Первоначально я использовал 2d Array "Tile" для хранения процедурно сгенерированной карты с ее различным содержимым.

Каждая плитка содержит смежный список, который позволяет каждой отдельной плитке узнать, какие вершины находятся ближе всего к ней, касаясь ее с 8 разных сторон (прямой и соседний по диагонали).

Общая идея была взята из генерации полигональных карт Амит, но я попытался упростить ее, используя сетку вместо воронуа, однако я столкнулся с большим количеством неприятностей, чем первоначально предполагалось. Мое текущее затруднительное положение заключается в том, чтобы выяснить смежность, когда я пересмотрел 2d Arrays.

Вот как я делал это, прежде чем перейти к списку:

private void ConstructAdjacencyList() {

    // Create Adjacency List
    for (int x = 0; x < mapWidth; x++) {
        for (int y = 0; y < mapHeight; y++) {
            // Bool to find position of point
            bool omitLeft = false;      bool omitRight = false;
            bool omitTop = false;       bool omitBottom = false;
            // Enable bools based on position, reset on each loop               
            if (x == 0)
                omitLeft = true;                
            else if (x == mapWidth - 1)
                omitRight = true;               
            if (y == 0)
                omitTop = true;             
            else if (y == mapHeight - 1)
                omitBottom = true;

            // Add entries to list based on bool settings
            if (!omitLeft) {
                // Left center
                islandMap[x,y].adjacent.Add(islandMap[x-1,y]);
                if (!omitTop)
                    islandMap[x,y].adjacent.Add(islandMap[x-1,y-1]);
                if (!omitBottom)
                    islandMap[x,y].adjacent.Add(islandMap[x-1,y+1]);
            }

            if (!omitTop) // Top Center
                islandMap[x,y].adjacent.Add(islandMap[x,y-1]);
            if (!omitBottom) // Bottom Center
                islandMap[x,y].adjacent.Add(islandMap[x,y+1]);

            if (!omitRight) {
                // Right Center
                islandMap[x,y].adjacent.Add(islandMap[x+1,y]);
                if (!omitTop)
                    islandMap[x,y].adjacent.Add(islandMap[x+1,y-1]);
                if (!omitBottom)
                    islandMap[x,y].adjacent.Add(islandMap[x+1,y+1]);
            }               
        }
    } // End Adjacency

    Debug.Log ("Adjacencies Built");
}

Значения x, y теперь хранятся в islandMap.point (Vector 2d, в котором хранятся значения x и y, сгенерированные следующим образом:)

public MapController() {
    width = height = (int)Mathf.Sqrt (tileCount);

    // Lists for points
    var points = new List<Vector2>();

    // Build a random set of points.
    for (float x = 0; x < width; x++)   {
        for (float y = 0; y < height; y++)  {
            points.Add(new Vector2(x,y));
        }
    }

    map = new Map (points, width, height, lakeTreshold);
}

И сама карта в настоящее время имеет следующее:

public class Map {
   Func<Vector2, bool> inside; // Contains function to randomly seed area
   bool needsMoreRandomness;

   public List<Tile> islandMap; // Previously was Tile[,] islandMap
   public int mapWidth { get; private set; } // Calculated as Sqrt(totalPoints)
   public int mapHeight { get; private set; }

Наряду с другими методами, такими как метод ConstructAdjacencyList(), на котором я сейчас остановился.

Итак, как я могу продолжить создание списка смежности окружающих точек, не полагаясь на позиционирование массива? Могу ли я временно ссылаться на весь список из массива, размещать ссылки на каждую плитку во всем списке в этом 2-мерном массиве, устанавливать смежности, а затем удалять массив без потери информации? Я полагаю, что он будет использовать только ссылки, так что все должно быть в порядке... Каждая плитка содержит индекс для хранения порядка, в котором она была построена следующим образом:

foreach (var point in points) {
        var p = new Tile { index = islandMap.Count, point = point };
        p.border = point.x == 0 || point.x == mapWidth || point.y == 0 || point.y == mapHeight;
        islandMap.Add (p);
        tileLookup[point] = p;
    }

Извините, если это слишком долго... Я только что понял, что это довольно массивно...

2 ответа

Предполагая, что вы ничего не сделали, чтобы изменить порядок точек, вы можете рассматривать одномерный список / массив как 2D список / массив. Пример, на который я ссылаюсь, написан на C, но язык идей не зависит.

При этом, предполагая, что вы будете делать это только один раз во время инициализации и учитывая относительно небольшое количество точек, вы можете так же легко перебрать его с помощью функции, которая перебирает список точек и выбирает соседей как необходимо. Мой C# немного ржавый, но я говорю о чем-то вроде этого:

private List<Vector2> getAdjacenctPointList(List<Vector2> pointsList, Vector2 point){
  var adjacencyList = new List<Vector2>();
  foreach (var pt in pointList){
    var offset = Math.abs(pt.x - point.x) + Math.abs(pt.y - point.y);
    if(offset > 0 && offset <= 1.0){
      adjacencyList.add(pt);
    }
  }
  return adjacencyList;
}

Окончательный ответ, который я получил, - это наименьший объем работы, который позволил мне переработать мой исходный код 2d-массива, опубликованный выше, - просто создать временный 2d-массив и сохранить в нем все ссылки - при необходимости создать все смежные области и затем избавьтесь (просто потеряв прицел) от 2-го массива.

Вот фактический метод с системой 2d массивов. Первые пара утверждений, за которыми следовал вложенный цикл for, - вот все, что нужно:

private void ConstructAdjacencyList() {
    Tile[,] tempArray = new Tile[mapWidth, mapHeight];

    int count = 0;

    // Populate the temp 2D array with list references
    for (int x = 0; x < mapWidth; x++) {
        for (int y = 0; y < mapHeight; y++) {
            tempArray[x,y] = islandMap[count];
            count++;            
        }
    }

    // Create Adjacency List using our TempArray
    for (int x = 0; x < mapWidth; x++) {
        for (int y = 0; y < mapHeight; y++) {
            // Bool to find position of point
            bool omitLeft = false;      bool omitRight = false;
            bool omitTop = false;       bool omitBottom = false;
            // Enable bools based on position, reset on each loop               
            if (x == 0)
                omitLeft = true;                
            else if (x == mapWidth - 1) // Optimize with if else to split checks in half.
                omitRight = true;               
            if (y == 0)
                omitTop = true;             
            else if (y == mapHeight - 1)
                omitBottom = true;

            // Add entries to list based on bool settings
            if (!omitLeft) {
                // Left center
                tempArray[x,y].adjacent.Add(tempArray[x-1,y]);
                if (!omitTop)
                    tempArray[x,y].adjacent.Add(tempArray[x-1,y-1]);
                if (!omitBottom)
                    tempArray[x,y].adjacent.Add(tempArray[x-1,y+1]);
            }

            if (!omitTop) // Top Center
                tempArray[x,y].adjacent.Add(tempArray[x,y-1]);
            if (!omitBottom) // Bottom Center
                tempArray[x,y].adjacent.Add(tempArray[x,y+1]);

            if (!omitRight) {
                // Right Center
                tempArray[x,y].adjacent.Add(tempArray[x+1,y]);
                if (!omitTop)
                    tempArray[x,y].adjacent.Add(tempArray[x+1,y-1]);
                if (!omitBottom)
                    tempArray[x,y].adjacent.Add(tempArray[x+1,y+1]);
            }               
        }
    } // End Adjacency

    Debug.Log ("Adjacencies Built");
}

Чтобы убедиться, что все получилось так, как я хотел, я проверил его визуально, установив один случайный куб, и проверил различные точки по всей области, чтобы убедиться, что ошибок не было и не было.

Конечный результат, как и ожидалось, изображен ниже:

Выбор элемента

Спасибо за помощь:)

Другие вопросы по тегам