Быстрое создание 2D ландшафта

от
GameDev    java

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

Но есть и более простой и быстрый способ генерирования 2D ландшафта.
Создаём массив размером с ширину игровой области.
int[] blockHeights = new int[width];
Определяем максимальную высоту, чтобы земля не вышла за пределы экрана.
int maxHeight = height / 2;
Рассчитываем значение, на которое будет изменяться высота при каждом шаге.
int stepHeight = maxHeight / 50;
Первому элементу массива задаём начальное значение в пределах от 0 до maxHeight.
blockHeights[0] = rnd.nextInt(maxHeight);
Далее, проходим по всему массиву от 1 до ширины и прибавляем к высоте предыдущего элемента случайное значение в пределах -stepHeight...stepHeight.
blockHeights[i] = blockHeights[i - 1] + rnd.nextInt(2 * stepHeight + 1) - stepHeight;
Также проверяем, чтобы высота всегда была в допустимых пределах.
if (blockHeights[i] > maxHeight) blockHeights[i] = maxHeight;
else if (blockHeights[i] < 0) blockHeights[i] = 0;


После этого мы получим нечто подобное.
  terrain_1.png

Но такая поверхность чересчур неровная, нужно сгладить её. Проще всего это сделать так: взять высоты двух соседних элементов и высчитать среднее арифметическое.
Но если сделать это один раз, то поверхность сгладится лишь ненамного. Поэтому увеличим количество итераций примерно до 10:
  1. final int smoothIterations = 10;
  2. for (int start = 1; start <= smoothIterations; start++) {
  3.     for (int i = start; i < blocksCount - 1; i += 2) {
  4.         blockHeights[i] = (blockHeights[i - 1] + blockHeights[i + 1]) / 2;
  5.     }
  6. }
Получим вот такую картину:
  terrain_2.png

На этом можно было бы остановиться, но для придания ландшафту большей реальности можно воспользоваться такой штукой как карта высот.
Подготовим изображение 1x100.
  heightmap.png

При инициализации конструктора класса Terrain, считаем это изображение и сгенерируем карту высот с цветом поверхности.

Java ME
  1. Image map = Image.createImage(getClass().getResourceAsStream("/heightmap.png"));
  2. final int mapSize = map.getHeight();
  3. heightMap = new int[mapSize];
  4. map.getRGB(heightMap, 0, 1, 0, 0, 1, mapSize);

Java SE
  1. BufferedImage map = ImageIO.read(getClass().getResourceAsStream("/heightmap.png"));
  2. final int mapSize = map.getHeight();
  3. heightMap = new Color[mapSize];
  4. for (int y = 0; y < mapSize; y++) {
  5.     heightMap[y] = new Color(map.getRGB(0, y));
  6. }

Для правильной отрисовки карты высот нам понадобится значение максимальной высоты поверхности. Это может быть высота игровой области, либо высчитанное максимальное значение при генерировании.

Выводится всё так:
  1. for (int i = 0; i < blocksCount; i++) {
  2.     final int blockHeight = blockHeights[i];
  3.     for (int j = 0; j <= blockHeight; j++) {
  4.         final int mapIndex = j * (heightMap.length - 1) / maxBlockHeight;
  5.         g.setColor(heightMap[mapIndex]);
  6.         g.fillRect(i, height - j, 1, 1);
  7.     }
  8. }

Результат:
  shot-20140315T130513.png

Проект (с исходниками и бинарниками): Java ME | PC
  • +11
  • views 6741