Доброго времени суток, Хабровчане! В этой статье я хочу рассказать про простой и быстрый способ генерации ландшафта. Прежде чем мы приступим к разбору самого алгоритма, хотелось бы отметить, что по отношению к генерации ландшафта мною данный алгоритм на просторах сети замечен не был, однако подобный алгоритм для генерации уровней был описан в статье, ссылка на которую будет в конце.
В какой ситуации удобен алгоритм
Недавно столкнулся с задачей: написать простую стратегию с трёхмерным ландшафтом. Так как я в данный момент обладаю маленьким опытом программирования на языке С++, мои попытки написать «diamond-square» закончились ошибками на ровном месте (ссылка на статью по «diamond-square» также будет в конце). Требовался простой в написании алгоритм, не дающий реалистичный ландшафт, так что данный метод поможет в первую очередь новичкам.
Алгоритм и результат
Прежде чем описывать сам алгоритм поделюсь его результатами:
Алгоритм заключается в том, что программа в случайных координатах заполняет карту прямоугольниками случайного размера. Карта имеет вид двухмерного массива, представляющего карту высот нашего ландшафта.
Для простоты создадим структуру прямоугольника:
struct tRect
{
int x1, y1, x2, y2;
}
Переменные x1 и y1 — левая нижняя координата прямоугольника, x2 и y2 — правая верхняя.
Пусть:
— Наша карта представлена в виде массива HM[mapsizex][mapsizey];
— mapsizey и mapsizex — переменные, определяющие размер вашей карты;
— genStep — переменная, отвечающая за количество наших прямоугольников;
— zscale — некий коэффициент растяжения карты в высоту. Можно заменить числом.
— recSizex и recSizey — пределы размеров прямоугольника.
Теперь необходимо заполнить нашу карту прямоугольниками:
for (int i=0; i<genStep; i++)
{
genRect.x1 = rand()%mapsizex;
genRect.y1 = rand()%mapsizey;
genRect.x2 = genRect.x1 + recSizex / 4 + rand()%recSizex;
genRect.y2 = genRect.y1 + recSizey / 4 + rand()%recSizey;
if (genRect.y2 > mapsizey) genRect.y2 = mapsizey;
if (genRect.x2 > mapsizex) genRect.x2 = mapsizex;
for (int i2 = genRect.x1; i2<genRect.x2; i2++)
for (int j2 = genRect.y1; j2<genRect.y2; j2++)
Map.HM[i2][j2]+= float(zscale) / float(genStep) + rand()%50 / 50.0;
}
Рельеф со скриншота был получен значениями:
genStep = 1024
zscale = 512
mapsizex и mapsizey = 128
recSize = 10
Далее вы выводите карту на экран любым доступным вам способом. В моём случае — openGl+glfw.
Преимущества и недостатки алгоритма
Преимущества:
- Простота и скорость в написании самого алгоритма
- Скорость исполнения алгоритма
Недостатки:
- Примитивность
- При маленьком шаге заполнения карты ландшафт становится «квадратным»
- Отсутствует возможность разбивать ландшафт на биомы по ходу генерации карты высот
Данный способ, как уже было сказано выше, подходит в первую очередь для новичков и людей, сильно ограниченных временем.
Надеюсь, данная статья была Вам полезной.
→ Статья про генерацию игровых уровней
→ Статья про «diamond-square»
Автор: Deathberry