Создание карты тайлов

25

Я программирую игру на основе плиток, и у меня есть несколько базовых плиток (трава, грязь и т. Д.), Но я не могу понять, как сделать хорошее генерирование случайных карт, потому что когда я делаю действительно случайный выбор, если плитка должна быть трава / грязь, я получаю это:

Картинка в игре

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

Желаемый результат

Вильда
источник
1
В дополнение к предложенным ответам, вы можете написать свой собственный механизм сотовых автоматов для создания таких карт. Модифицируя правила автомата, вы можете генерировать совершенно разные варианты поведения, приводя к совершенно разным типам карт. Например, если у вас 2d автомат, похожий на Life, правила затопления могут привести к «океану и островам» (как на вашей картинке выше), а правило, подобное 1267/17, может привести к прекрасным лаберинтам.
Manu343726

Ответы:

19

То, что вы могли бы сделать, это случайно сгенерировать карту Вороного, например:

  1. Выбирайте случайно center points(см. Черные точки) и случайным образом решаете, являются ли они травой или грязью.
  2. Затем по всем плиткам проверьте, не ближе ли он к center pointгрязи или траве.
  3. Выполнено!

Если то, что вы сделали ранее, это «подбросить монету» для каждой плитки (шума), генерирование диаграммы Вороного даст гораздо лучший результат.

Вы можете улучшить это, разделив center pointsна islandsс помощью алгоритма, который:

  1. Выбирает небольшую группу centers pointsи обозначает их как leaders.
  2. Итеративно добавляет случайную смежную неопределенную центральную точку каждый ход.
  3. Выполнено!

введите описание изображения здесь

wolfdawn
источник
1
Спасибо, но это кажется очень сложным решением.
Вилда
2
Это не очень сложное решение. Сначала случайно выбери center points. Затем решите, являются ли они травой или грязью. Теперь переберите массив и решите, находится ли каждая плитка ближе к точке грязи или траве. Возможно, скажите мне, какая часть сложная?
волчий рассвет
Измерение расстояния. Во всяком случае, я постараюсь дать вам знать.
Вилда
Итак, я успешно заработал несколько очков. ( dropbox.com/s/dlx9uxz8kkid3kc/random_tile_map2.png ) и поэтому весь мой класс для генерации выглядит следующим образом: dropbox.com/s/pxhn902xqrhtli4/gen.java . Но это все еще не работает.
Вилда
3
@ViliX ваши ссылки не работают
Артур Цайка,
24

Вы можете использовать перлин-шум, который обычно используется для генерации карты высот. Перлинский шум в играх

Тогда вы можете использовать высоты в качестве советника, насколько высока вероятность появления травы / грязи в одном регионе карты.

Пример (значения шума Перлина от 0 до 256): если значение превышает 200, вероятность того, что трава будет размещена, составляет 80% (грязь 20%). Если значение составляет от 100 до 200, вероятность размещения травы составляет 50% (грязь также составляет 50%). Если значение меньше 100, вероятность размещения травы составляет 20% (грязь 80%).

Klitz
источник
Хорошо, допустим, у меня есть массив [] [] с плавающей точкой (0-1 вероятности), и я могу использовать его для создания тайлов с вероятностью. Но как мне заполнить этот массив вероятностей? Массив (скажем так) 400x200, как заполнить его значениями вероятности?
Вилда
Он предлагает заполнить его перлиновым шумом (вместо белого шума, как монеты).
волчий рассвет
Да, но как мне этого добиться?
Вилда
Ссылка, которую я разместил, может прояснить этот вопрос. Сначала создайте шум, а затем сгладьте этот шум. В результате вы получите двухмерный массив, который вы можете использовать для дальнейшего генерирования карты тайлов. ссылка
Клиц
Это действительно хороший ответ, но он редко дает результаты, подобные представленным вами.
волчий рассвет
8

введите описание изображения здесь

http://gamedevelopment.tutsplus.com/tutorials/generate-random-cave-levels-using-cellular-automata--gamedev-9664

вот моя версия метода клеточных автоматов, начните с заполнения сетки случайным образом, затем выполните эти правила клеточных автоматов несколько раз

  • Если у живой клетки меньше двух живых соседей, она умирает.
  • Если у живой клетки есть два или три живых соседа, она остается живой.
  • Если у живой клетки более трех живых соседей, она умирает.
  • Если у мертвой клетки ровно три живых соседа, она становится живой.

и это в конечном итоге выглядит как пещера

индекс может быть преобразован в позицию x & y и обратно с этим кодом

public int TileIndex(int x, int y)
{
    return y * Generator.Instance.Width + x;
}
public Vector2 TilePosition(int index)
{
    float y = index / Generator.Instance.Width;
    float x = index - Generator.Instance.Width * y;
    return new Vector2(x, y);
}

я просто возвращаю список bools, потому что я использую этот список для многих вещей: пещеры, деревья, цветы, трава, туман, вода, вы можете даже комбинировать несколько списков здесь, сначала я удаляю все меньшие пещеры, затем объединяю два случайных списка

введите описание изображения здесь

private int GetAdjacentCount(List<bool> list, Vector2 p)
{
    int count = 0;
    for (int y = -1; y <= 1; y++)
    {
        for (int x = -1; x <= 1; x++)
        {
            if (!((x == 0) && (y == 0)))
            {
                Vector2 point = new Vector2(p.x + x, p.y + y);
                if (PathFinder.Instance.InsideMap(point))
                {
                    int index = PathFinder.Instance.TileIndex(point);
                    if (list[index])
                    {
                        count++;
                    }
                }
                else
                {
                    count++;
                }
            }
        }
    }
    return count;
}
private List<bool> GetCellularList(int steps, float chance, int birth, int death)
{
    int count = _width * _height;
    List<bool> list = Enumerable.Repeat(false, count).ToList();
    for (int y = 0; y < _height; y++)
    {
        for (int x = 0; x < _width; x++)
        {
            Vector2 p = new Vector2(x, y);
            int index = PathFinder.Instance.TileIndex(p);
            list[index] = Utility.RandomPercent(chance);
        }
    }
    for (int i = 0; i < steps; i++)
    {
        var temp = Enumerable.Repeat(false, count).ToList();
        for (int y = 0; y < _height; y++)
        {
            for (int x = 0; x < _width; x++)
            {
                Vector2 p = new Vector2(x, y);
                int index = PathFinder.Instance.TileIndex(p);
                if (index == -1) Debug.Log(index);
                int adjacent = GetAdjacentCount(list, p);
                bool set = list[index];
                if (set)
                {
                    if (adjacent < death)
                        set = false;
                }
                else
                {
                    if (adjacent > birth)
                        set = true;
                }
                temp[index] = set;
            }
        }
        list = temp;
    }
    if ((steps > 0) && Utility.RandomBool())
        RemoveSmall(list);
    return list;
}
Rakka Rage
источник
2
Пожалуйста, объясните, что делает ваш код, а не публикуйте образец того, что он делает, ссылку и сам код. Ваш ответ должен быть самодостаточным, чтобы даже если ссылки были разорваны, его можно было использовать.
Фонд Моника иск
6

Выберите точку на карте. Поместите желаемый тип плитки с базовым значением, таким как 40. Следите за тем, где вы разместили свою новую желаемую плитку. Добавьте отправную точку в список.

Для каждой точки в этом списке вы посещаете всех соседей. Пока у вас осталось достаточно энергии (начиная с 40), добавьте желаемую плитку и добавьте ее в список для посещения. Дайте новой плитке меньше мощности, определенной вами. Самый простой = случайное понижение. После того, как вы посетили плитку из списка, удалите ее. Начните сначала, посетив любые не посещенные, но созданные плитки.

Jaapjan
источник