- Основные понятия
- Глобальные переменные
- Файл конфигурации
- Фрагменты
- Структуры
- Биомы
- Heightmap (карта высот)
- VoxelFragment (фрагмент)
- Генерация карты высот
- Ручная расстановка структур
- Структурный воздух
- Генератор 'Demo' (base:demo)
Понятия используемые далее по тексту.
- Комбинируемый массив/объект - TOML или JSON файл, который комбинируется из нескольких версий в разных паках, что позволяет добавлять в него данные извне. Поля комбинированного объекта перезаписываются в порядке от первого к последнему, так же, как и другие ресурсы в паках. В случае комбинируемого массива, проверка на дубликаты не выполняется.
- Биом - информация, определяющая то, из каких блоков и какими слоями генерируется ландшафт, а так же набор растений, структур.
- Растение - случайно расставляемый на поверхности блок.
- Малая структура - структура, размер которой не превышает размера чанка. Пример: деревья.
Генератор мира распознается при наличии файла generators/имя_генератора.toml
. Другие файлы, относящиеся к генератору, должны находиться в директории generators/имя_генератора.files/
:
- biomes.toml - объявления биомов
- structures.toml - объявления структур
- script.lua - скрипт генератора
- fragments - директория в которой располагаются файлы фрагментов
Основные свойства, описываемые в файле конфигурации:
- caption - отображаемое имя генератора. По-умолчанию генерируется из id.
- biome-parameters - количество параметров выбора биомов (от 0 до 4). По-умолчанию: 0.
- sea-level - уровень моря (ниже этого уровня вместо воздуха будут генерироваться слои моря (sea-layers)). По-умолчанию: 0.
- biomes-bpd - количество блоков на точку карты параметра выбора биомов. По-умолчанию: 4.
- heights-bpd - количество блоков на точку карты высот. По-умолчанию: 4.
- wide-structs-chunks-radius - масимальный радиус размещения 'широких' структур, измеряемый в чанках.
- heightmap-inputs - массив номеров карт параметров, которые будут переданы таблицей inputs в функцию генерации карты высот.
В скрипте генератора доступны следующие переменные:
SEED
- зерно генерации мира__DIR__
- директория генератора (пак:generators/имя_генератора.files/
)__FILE__
- файл скрипта (пак:generators/имя_генератора.files/script.lua
)
Фрагмент является сохраненной для дальнейшего использования, областью мира, как и чанк, ограниченную некоторой шириной, высотой и длиной. Фрагмент может содержать данные не только о блоках, попадающих в область, но и о инвентарях блоков области, а так же сущностях. В отличие от чанка, размер фрагмента произволен.
На данный момент, фрагмент может быть создан при помощи команды fragment.save
, либо через функцию generation.create_fragment
.
Фрагменты, используемые генератором, должны находиться в директории:
generators/имя_генератора.files/fragments/
Структура - набор правил по вставке фрагмента в мир генератором. Структуры объявляются в виде объектов в файле generators/имя_генератора.files/structures.toml
. Пример:
tree0 = {}
tree1 = {}
tree2 = {}
tower = {lowering=-2}
coal_ore0 = {}
На данный момент, имя структуры должно совпадать с именем использованного фрагмента.
Доступные свойства:
- lowering - глубина погружения структуры под поверхность.
Биом определяет то, из каких блоков и какими слоями генерируется ландшафт, а так же набор растений, структур.
Биомы объявляются в комбинируемом объекте:
generators/имя_генератора.files/biomes.toml
Разберем структуру биома на примере леса из генератора base:demo:
[forest]
parameters = [
{weight=1, value=1},
{weight=0.5, value=0.2}
]
layers = [
{below-sea-level=false, height=1, block="base:grass_block"},
{below-sea-level=false, height=7, block="base:dirt"},
{height=-1, block="base:stone"},
{height=1, block="base:bazalt"}
]
sea-layers = [
{height=-1, block="base:water"}
]
plant-chance = 0.4
plants = [
{weight=1, block="base:grass"},
{weight=0.03, block="base:flower"}
]
structure-chance = 0.032
structures = [
{name="tree0", weight=1},
{name="tree1", weight=1},
{name="tree2", weight=1},
{name="tower", weight=0.002}
]
- ключ forest - имя биома
- parameters - веса и центральные значения параметров для биома. См. раздел выбор биома. Количество записей должно соответствовать количеству параметров выбора биомов.
- layers - слои блоков от верхнего к нижнему.
- height - высота слоя в блоках. -1 используется для обозначения безразмерного (заполняющего) слоя, который может быть только один. Его высота вычисляется автоматически.
- block - полное имя блока
- below-sea-level - может ли слой быть сгенерированным ниже уровня моря (пример: дёрн). При значении false, при генерации ниже уровня моря, слой будет заменён на следующий.
- sea-layers - слои океана. Положение верхнего слоя совпадает с высотой уровня моря.
- plant-chance - вероятность генерации растения на блоке поверхности.
- plants - растения, случайно расставляемые на поверхности.
- weight - вес, напрямую влияющий на шанс выбора конкретного растения.
- block - блок растения
- structure-chance - вероятность генерации малой структуры на блоке поверхности.
- structures - структуры, случайно расставляемые на поверхности.
- name - имя структуры, объявленной в
structures.toml
. - weight - вес, напрямую влияющий на шанс выбора конкретной структуры.
- name - имя структуры, объявленной в
Параметр генератора biome-parameters
определяет количество параметров биомов, используемых при их выборе (примеры: температура, влажность).
Карты значений параметров биомов генеруются так же, как и карты высот.
Требуется реализовать функцию:
-- x, y - позиция начала карты (в точках)
-- w, h - ширина и высота карты (в точках)
-- bpd - (blocks per dot) число блоков на точку (масштаб)
function generate_biome_parameters(x, y, w, h, bpd)
-- создание карт высот (Heightmap) для каждого параметра биомов
-- ...
return карты_через_запятую
end
-- пример
function generate_biome_parameters(x, y, w, h, s)
-- карта температур
local tempmap = Heightmap(w, h)
tempmap.noiseSeed = SEED + 5324
tempmap:noise({x, y}, 0.04*s, 6)
tempmap:pow(3)
-- карта влажности
local hummap = Heightmap(w, h)
hummap.noiseSeed = SEED + 953
hummap:noise({x, y}, 0.04*s, 6)
hummap:pow(3)
return tempmap, hummap
end
После генерации карт параметров для каждого биома вычисляются оценки по всем параметрам:
Где
Генератор выбирает биом с наименьшей суммой оценок для параметров.
Warning
При неудачной настройке значений и весов параметров у биомов может возникать эффект, имеющий ту же природу, что и конфликт глубины в 3D графике при наложении двух поверхностей.
В случае биомов, узор выглядит случайным из-за искривления этих 'поверхностей' шумом, используемым при генерации карт параметров.
Для избавления от эффекта можно либо скорректировать веса или значения параметров биомов, либо увеличить разницу в генерации карт параметров.
Heightmap это класс для работы с картами высот (матрицами чисел с плавающей точкой произвольного размера).
Конструктор карты высот требует указания целочисленных ширины и высоты.
local map = Heightmap(ширина, высота)
Операции применяются ко всем значениям высоты.
map:abs()
Приводит значения высот к абсолютным.
Операции с применением второй карты или скаляра.
Арифметические операции:
-- Прибавление
map:add(value: Heightmap|number)
-- Вычитание
map:sub(value: Heightmap|number)
-- Умножение
map:mul(value: Heightmap|number)
-- Возведение в степень
map:pow(value: Heightmap|number)
Другие операции:
-- Минимум
map:min(value: Heightmap|number)
-- Максимум
map:max(value: Heightmap|number)
-- Примешивание
map:mixin(value: Heightmap|number, t: Heightmap|number)
-- t - фактор смешивания от 0.0 до 1.0
-- смешивание производится по формуле:
-- map_value * (1.0 - t) + value * t
Метод используемый для отладки, создает изображение на основе карты высот переводя значения из дипазона [-1.0, 1.0]
в значения яркости [0, 255]
, сохраняя в указанный файл.
map:dump('export:test.png')
Метод генерирующий симплекс-шум, прибавляя его к имеющимся значениям.
Зерно шума может быть указано в поле map.noiseSeed
.
map:noise(
-- смещение координат
offset: {number, number},
-- коэфициент масштабирования координат
scale: number,
-- число октав шума (по-умолчанию: 1)
[опционально] octaves: integer,
-- множитель амплитуды шума (по-умолчанию: 1.0)
[опционально] multiplier: number,
-- карта смещений координаты X при генерации шума
[опционально] shiftMapX: Heightmap,
-- карта смещений координаты Y при генерации шума
[опционально] shiftMapY: Heightmap,
) -> nil
Визуализация шума с октавами 1, 2, 3, 4 и 5.
Аналог heightmap:noise генерирующий клеточный шум.
Зерно шума может быть указано в поле map.noiseSeed
.
map:resize(ширина, высота, интерполяция)
Изменяет размер карты высот.
Доступные режимы интерполяции:
- 'nearest' - без интерполяции
- 'linear' - билинейная интерполяция
- 'cubic' - бикубическая интерполяция
map:crop(x, y, ширина, высота)
Обрезает карту высот до заданной области.
map:at(x, y) --> number
Возвращает значение высота на заданной позиции.
Фрагмент создается вызовом функции:
generation.create_fragment(
-- точка A
a: vec3,
-- точка B
b: vec3,
-- автоматически обрезать фрагмент, если возможно
crop: bool
) -> VoxelFragment
Фрагмент может быть загружен из файла:
generation.load_fragment(
-- файл фрагмента
filename: str
) -> VoxelFragment
Фрагмент может быть сохранен в файл:
generation.save_fragment(
-- сохраняемый фрагмент
fragment: VoxelFragment,
-- файл
filename: str
) -> nil
Размер фрагмента доступен как свойство size
.
-- Обрезает фрагмент до размеров содержимого
fragment:crop()
-- Устанавливает фрагмент в мир на указанной позиции
fragment:place(position: vec3, [опционально] rotation:int=0)
По-умолчанию, движок генерирует карту высот, состоящую из нулей.
Для генерации пользовательских карт высот требуется реализовать функцию:
function generate_heightmap(
x, y, -- смещение карты высот
w, h, -- размер карты высот, ожидаемый движком
bpd, -- число блоков на точку карты (blocks per dot) - масштаб
[опционально] inputs -- массив входных карт параметров биомов
-- (см. свойство heightmap-inputs генератора)
) --> Heightmap
Пример генерации карты высот из простого симплекс-шума с приведением к нужному диапазону:
function generate_heightmap(x, y, w, h, bpd)
-- создаем карту высот с заданным размером
local map = Heightmap(w, h)
-- настраиваем зерно шума
map.noiseSeed = SEED
-- шум с масштабом 1/10 на 4 октавы с амплитудой 0.5
map:noise({x, y}, 0.1*bpd, 4, 0.5)
-- сдвигаем высоты к положительному диапазону
map:add(0.5)
return map
end
Размещение структуры / линии представляет собой массив из заданного набора параметров.
Структура:
{имя_структуры, позиция_структуры, поворот, [опционально] приоритет}
Где:
- имя_структуры - строка содержащая имя структуры, зарегистрированная в structures.toml.
- позиция_структуры - vec3 (массив из трех чисел) относительно позиции чанка.
- поворот - число от 0 до 3 обозначающая поворот структуры по оси Y.
- приоритет - число определяющее порядок установки структур. Структуры с меньшем приоритетом перекрываются структурами с большим.
Тоннель:
{":line", блок_заполнитель, точка_а, точка_б, радиус}
Где:
- блок_заполнитель - числовой id блока, из которого будет состоять структура.
- точка_а, точка_б - vec3, vec3 позиции начала и конца тоннеля.
- радиус - радиус тоннеля в блоках
function place_structures(
x, z, -- позиция начала области в блоках
w, d, -- размер области в блоках
heights, -- карта высот чанка
chunk_height, -- высота чанка
) --> массив размещений структур
Структуры могут размещаться за пределами чанка, но не дальше, чем на один чанк.
Пример:
function place_structures(x, z, w, d, hmap, chunk_height)
local placements = {}
local height = hmap:at(w/2, h/2) * chunk_height
-- устанавливает башню по центру чанка
table.insert(placements, {
'tower', {w/2, height, d/2}, math.random() * 4, 2
})
return placements
end
Структуры и тоннели могут размещаться за пределами чанка, но не дальше, чем на число чанков, указанное в свойстве генератора wide-structs-chunks-radius
.
В отличие от прошлой функции, сюда не передается карта высот, так как вызов происходит на ранних этапах генерации чанка.
function place_structures_wide(
x, z, -- позиция начала области в блоках
w, d, -- размер области в блоках
chunk_height, -- высота чанка
) --> массив размещений структур / тоннелей
core:struct_air
- блок, которые следует использовать в фрагментах для обозначения пустого пространства, которое не должно заполняться блоками при генерации в мире.
Чтобы добавить новую руду из своего пака:
- В папке
generators
создайте папкуdemo.files
(demo.toml создавать не нужно). - В созданной папке создайте папку fragments и поместите в неё файл фрагмента руды.
- В
demo.files
создайте файл structures.toml:
имя_фрагмента = {}
- Также в
demo.files
создайте файл ores.json:
[
{"struct": "имя_фрагмента", "rarity": редкость}
]
Чем выше значение редкости, тем меньше вероятность генерации руды. Опираться можно на редкость угольной руды: 4400.