Быстрое создание 2D ландшафта
от aNNiMON
При написании некоторых игр возникает необходимость создать холмистый ландшафт. Есть множество различных алгоритмов таких как, например, разбиение пополам: берётся прямая линия на всю ширину игровой области, затем берётся середина этой линии и поднимается или опускается на некоторую величину. Затем полученные две линии снова делятся пополам и изменяются на некоторую величину в центральной точке и так до тех пор, пока размер разделённой пополам линии не достигнет какого-то предела, обычно в один пиксель.
Но есть и более простой и быстрый способ генерирования 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;
После этого мы получим нечто подобное.
Но такая поверхность чересчур неровная, нужно сгладить её. Проще всего это сделать так: взять высоты двух соседних элементов и высчитать среднее арифметическое.
Но если сделать это один раз, то поверхность сгладится лишь ненамного. Поэтому увеличим количество итераций примерно до 10:
Получим вот такую картину:
На этом можно было бы остановиться, но для придания ландшафту большей реальности можно воспользоваться такой штукой как карта высот.
Подготовим изображение 1x100.
При инициализации конструктора класса Terrain, считаем это изображение и сгенерируем карту высот с цветом поверхности.
Java ME
Java SE
Для правильной отрисовки карты высот нам понадобится значение максимальной высоты поверхности. Это может быть высота игровой области, либо высчитанное максимальное значение при генерировании.
Выводится всё так:
Результат:
Проект (с исходниками и бинарниками): Java ME | PC
Но есть и более простой и быстрый способ генерирования 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;
После этого мы получим нечто подобное.
Но такая поверхность чересчур неровная, нужно сгладить её. Проще всего это сделать так: взять высоты двух соседних элементов и высчитать среднее арифметическое.
Но если сделать это один раз, то поверхность сгладится лишь ненамного. Поэтому увеличим количество итераций примерно до 10:
- final int smoothIterations = 10;
- for (int start = 1; start <= smoothIterations; start++) {
- for (int i = start; i < blocksCount - 1; i += 2) {
- blockHeights[i] = (blockHeights[i - 1] + blockHeights[i + 1]) / 2;
- }
- }
На этом можно было бы остановиться, но для придания ландшафту большей реальности можно воспользоваться такой штукой как карта высот.
Подготовим изображение 1x100.
При инициализации конструктора класса Terrain, считаем это изображение и сгенерируем карту высот с цветом поверхности.
Java ME
- Image map = Image.createImage(getClass().getResourceAsStream("/heightmap.png"));
- final int mapSize = map.getHeight();
- heightMap = new int[mapSize];
- map.getRGB(heightMap, 0, 1, 0, 0, 1, mapSize);
Java SE
- BufferedImage map = ImageIO.read(getClass().getResourceAsStream("/heightmap.png"));
- final int mapSize = map.getHeight();
- heightMap = new Color[mapSize];
- for (int y = 0; y < mapSize; y++) {
- heightMap[y] = new Color(map.getRGB(0, y));
- }
Для правильной отрисовки карты высот нам понадобится значение максимальной высоты поверхности. Это может быть высота игровой области, либо высчитанное максимальное значение при генерировании.
Выводится всё так:
- for (int i = 0; i < blocksCount; i++) {
- final int blockHeight = blockHeights[i];
- for (int j = 0; j <= blockHeight; j++) {
- final int mapIndex = j * (heightMap.length - 1) / maxBlockHeight;
- g.setColor(heightMap[mapIndex]);
- g.fillRect(i, height - j, 1, 1);
- }
- }
Результат:
Проект (с исходниками и бинарниками): Java ME | PC