Отображение этих двух пространств друг в друга искажает углы. Так, переход из вершин на экран, описывается матрицей вида:
<note tip> ( 48, 24 )
(0, 30)</note>
И обратное:
<note tip> ( 1/48, -1/24 )
(0, 1/30)</note>
Квадрат, нарисованный в одном пространстве, в другом отображается как ромб (параллелограмм). Например, прямоугольная область пространства вершин, если двигаться строго сверху-вниз, на экране будет ощутимо уползать влево. Если же вести ровно пальцем по экрану сверху-вниз, то прообраз пальца в пространстве вершин будет двигаться вниз-вправо.
Так как нам удобнее всегда работать с прямоугольниками, а пляшем мы всё-таки от экранного представления ландшафта, то что бы в пространстве вершин стало всё хорошо, нужно прообраз прямоугольного ландшафта, ставший в пространстве вершин ромбом, дополнить уголками слева и справа, что бы он там был прямоугольным. Эти лишние уголки на экране просто не показываются, можно их не хранить вообще нигде, но считать что как бы пространство вершин их содержит.
Такой прямоугольник в пространстве вершин, имеет избыточность порядка +50% относительно реально нужной площали, и квадратный мир в виде вершин может состоять из прямоугольника с соотношением сторон почти равным 3/2.
Избыточный прямоугольник потому и избыточный - что не все его точки нам нужны. Те точки пространства вершин, которые попадают внутрь нужной нам области ландшафта, называем “живыми”, остальные - “мёртвыми”. Вообще говоря, эти вопросы являются лишь проблемой генератора - именно он должен заботиться о том, что бы суша располагалась только в “живой” области, а “мёртвая” не должна содержать ничего интересного, так как клиент её просто физически не способен отрисовать.
На них можно строить чанки, однако клиент их отобразить всё равно не сможет. появились из-за того, что пограничные чанки ландшафта иногда располагаются прямо на границе разрешенной к отображению области, то есть граница “живого” и “мёртвого” проходит посреди чанка, а резать его не хочется.
Важный параметр - количество суши в ландшафте. Считается простой суммой вершин, со значением высоты не равной константе “дно морское”. Отсюда видно, что вершины под водой, но выше дна являются паразитами - отъедают ресурсы от реальной суши, потому желательно их сделать немногочисленными. Тривиальное решение ограничить “дно” значением -1 не подходит, так как выровняет везде угол ухода суши под воду. Подробнее об этом в разделе “море и берег”
Часть океана вокруг ландшафта (мира), откуда мы разрешим корабликам уплывать в другие миры. Обязательно должна быть связной, то есть полностью содержать в себе всю сушу ландшафта.
Главная наша функция показывает, с какого горизонтального индекса начинается и заканчивется строка “живых и полу-живых” вертексов в “избыточном” пространстве:
<note tip>xmin(y) = ( (y - 1 ) / 2D ) * D
xmax(y) = ( y / 2D ) * D + length</note>
где D - диаметр чанка в вершинах, а length = ширина чанкуемого ландшафта в вершинах
Если не заметили, не все строки “живых” вершин равны по длине. Почему так - должно быть видно на рисунке.
Всё довольно просто: метрика всем уже исвестна, а вот и формулы определяющие принадлежность точки (x,y) к “живым и полу-живым” вершинам:
<note tip>y >= 0 && y < height
&&
x >= xmin(y) && x < xmax(y)</note>
только не забывайте что вершин на одну больше чем фрагментов, и если у вас 1024 фрагмента, то нужно включить вершину с индексом [1024], ведь вершин у вас 1025 штук.
Важно: вообще это всё головная боль генератора - именно он должен обеспечить, что будут использованы только “живые” точки, и все нужные “зоны переходов” будут там где надо. Клиенту можно поступить проще - загрузить данные с сервера прямо в избыточный массив вершин, и строить чанки тупо циклом.
Тоже важно: такие вольности, как треть сусорных вершин в буфере допустимы потому, что всё равно в них мусор. Когда таскать мусор надоест, можно переписать код и не слать их. Но вообще-то обычный алгоритм архивации сделает это за нас, так как он мастер находить нулевые последовательности в строках
Чанки повторяют по форме треугольники, только крупнее. И так же как и треугольники, мы их разделим на “красные” и “зелёные”. Что бы не мучаться, будем работать сразу с парами чанков, “кр+зел”, исключив случаи когда у кого-то нет пары. Правда цена тому - несколько однозначно невидимых никогда чанков на левой и правой границе области… ну и чёрт с ними!
Максимальный размер мира определяется двояко:
Первое условие вводится по большей части для удобства. Так как нам никто не мешает вытянуть ландшафт в длинную колбаску или бублик, теоретически, полный квадрат ландшафта может быть и не квадрат.. однако что бы упростить себе жизнь - на построении минимапов, и в многих других мелочах, будем использовать это жёсткое ограничение. Так же важно, что из-за кривой (параллелограмм) координатной сетки вертексов в мире, можно работать с прямоугольником 3Кх2К в пространстве вершин, от которого потом будут оттяпаны углы, как показано на рисунке справа.
Второе условие может строго не соблюдаться, однако большие отклонения в любую сторону от указанной цифры нежелательны. Ландшафт лучше пакуется, когда в нём больше моря, да и балансируется игра так что бы на каждого игрока приходилось определённое количество суши, и не хочется выяснять, как заклинит баланс, если суши не хватит или будет слишком много.
Третье условие должно выполняться потому, что во-первых, нужно каждый мир окружить водой - что бы в него с любой стороны можно было приплыть. Во-вторых, надо как-то отображать край карты на клиенте, и легче это сделать когда узкая полоса на границе (которая у нас рваная) отдана под мёртвую зону,и скроллинг туда невозможен.
Так как мы пренебрегаем краем ландшафта, и там у нас гарантированно вода, то ландшафт можно незаметно зациклить и горизонтально и вертикально, что сделает количество фрагментов и вершин равным. И вершин станет “хорошее число”. Однако на клиенте при построении геометрии придётся использовать деление по модулю… а оно нам надо?
<note important>С версии от 4.12.2011, подводными треугольниками можно пренебречь. Однако вопрос ещё не закрыт - можно поэкспериментировать с водой</note>
Сейчас моря реализованы так: если треугольник ландшафта имеет хоть одну точку ниже уровня 0, рисуется треугольник лежащий ровно на уровне 0, и выглядящий как синяя блямба. Запрещены треугольники, с гранями по разные стороны высоты 0. Из этого однозначно следует, что уровень моря (высота 0) весь представлен сплошной изо-линией, что можно уже увидеть в текущих версиях клиента.
Серевер обрабатывает модификаторы текстур в развёрнутом виде - ему так удобно. Клиенту же нужен только результат - то есть какой вариант текстурирования применить. Общий вид алгоритма:
Правило: MODS.BURNED > MODS.SNOW > MODS.GRASS То есть, когда включается BURNED не важно уже что там в остальных полях, клиенту по крайней мере.
struct modifiers { int2 grass; int2 snow; int2 burned; } assert( grass >= 0 && grass < 4 ) assert( snow >= 0 && snow < 3 ) assert( burned >= 0 && burned < 3 )
ID | Name |
---|---|
0 | DEFAULT |
1 | GRASS1 |
2 | GRASS2 |
3 | GRASS3 |
4 | SNOW1 |
5 | SNOW2 |
6 | BURNED1 |
7 | BURNED2 |
Базовый тип | name | ID | нет модиф. | слабая трава | сильная трава | полная трава | слабый снег | полный снег | слабо выжжено | полностью выжжено |
---|---|---|---|---|---|---|---|---|---|---|
DEFAULT | GRASS-1 | GRASS-2 | GRASS-3 | SNOW-1 | SNOW-2 | BURNED-1 | BURNED-2 | |||
полный декоратор | –/– | –/– | –/– | –/– | –/– | decor.grass | –/– | decor.snow | –/– | decor.burned |
Дно моря | BOTTOM | 0 | bottom.default | bottom.default | bottom.default | bottom.default | bottom.snowWeak | bottom.snowWeak | bottom.default | bottom.default |
Скала | CLIFF | 1 | cliff.default | cliff.grassWeak | cliff.grassWeak | cliff.grassWeak | cliff.snowWeak | cliff.snowHard | cliff.burnedWeak | decor.burned |
Каменистый | STONE | 2 | stone.default | stone.grassWeak | stone.grassMed | stone.grassMed | stone.snowWeak | decor.snow | stone.burnedWeak | decor.burned |
Сухой грунт | DRY | 3 | dry.default | dry.grassWeak | dry.grassMed | decor.grass | dry.snowWeak | decor.snow | dry.burnedWeak | decor.burned |
Мягкий грунт | SOFT | 4 | soft.default | soft.grassWeak | soft.grassMed | decor.grass | soft.snowWeak | decor.snow | soft.burnedWeak | decor.burned |
Грязь(болото) | DIRT | 5 | dirt.default | dirt.grassMed | dirt.grassMed | dirt.grassMed | dirt.snowWeak | decor.snow | dirt.burnedWeak | decor.burned |
Песок | SAND | 6 | sand.default | sand.default | sand.default | sand.grassHard | sand.snowWeak | sand.snowWeak | sand.default | sand.burnedHard |
Комментарии к таблице:
Каждая текстура в сете имеет “альтерации”, то есть набор текстур с одним именем, и дополнительным расширением (цифра). Например, текстуры snow.png нет. Есть только текстуры snow.0.png или snow.1.png
При отрисовке номер альтерации берётся из константы вершины “номер альтерации”
Итак, зная базовую текстуру и набор модификаторов, выбрав из модификатор один с максимальным приоритетом, получаем из таблицы текстуру. Там три варианта:
Статические данные хранят базовый тип и альтерацию. Это три бита + 1 бит:
T | A | S | type | filename |
---|---|---|---|---|
0 | 0 | 0 | BOTTOM | bottom.default.0.png |
1 | 8 | bottom.default.1.png | ||
1 | 0 | 1 | CLIFF | cliff.default.0.png |
1 | 9 | cliff.default.1.png | ||
2 | 0 | 2 | STONE | stone.default.0.png |
1 | 10 | stone.default.1.png | ||
3 | 0 | 3 | DRY | dry.default.0.png |
1 | 11 | dry.default.1.png | ||
4 | 0 | 4 | SOFT | soft.default.0.png |
1 | 12 | soft.default.1.png | ||
5 | 0 | 5 | DIRT | dirt.default.0.png |
1 | 13 | dirt.default.1.png | ||
6 | 0 | 6 | SAND | sand.default.0.png |
1 | 14 | sand.default.1.png |
Начиная с 4 декабря, в динамические данные ландшафта засунули целый байт, отвечающий за “декоратор” данной точки. Частным случаем декораторов являются “ресурсные точки”, деревья - так же частный случай декоратора. Особо нервных просьба не паниковать, размер вершины не вырос и остался равным 16 бит. Из динамики неё выброшены четыре бита, отвечавшие за деревья, и мы по-прежнему даже имеем резерв для дополнительных высот или чего ещё.
Вообще, декораторы угодили в динамические объекты неспроста: их свойства никак не позволяют отнести их к статике, а как полноценные объекты их реализовывать не очень эффективно - не думаю что нам нужен “лишний” список из сотни тысяч объектов, и каждый с позицией в 8 байт. В лучае подселения декораторов в вершины мы эти самые 100.000 * 8 байт и сэкономим.
Декораторы могут быть такими:
То есть через всё у чего индекс меньше 64 перелезть можно.. этот факт может прмочь при построении карты проходимости местности на клиенте *Внимание* деревья ,как ресурсы разбежались в две группы: “мусорные деревья” и пробрались в группу “непроходимые ресурсы”. Это неслучайно: нам нельзя засаживать ценными породами деревьев всю карту, и лысой её держать не хочется. Потому, “мусорные деревья” поставляют базовую “древесину”, а всё что лучше неё - редкие вкрапления декораторов из группы “непроходимые ресурсы”.
Исключительно в декоративных целях, можно отрисовывать один и тот же декоратор в разных вариантах. Дла этого мы когда-то ввели в статическую часть вертекса параметр “номер набора декораторов”. Те декораторы, которым вариации не нужны, могут игнорировать этот параметр - ничего страшного, лежит, не мешает себе. Сейчас допустимое число вариаций для каждого декоратора должно нацело делиться на 16 и быть не больше 16.
Любой декоратор управляется сервером. Игрок может не ждать сервер и уничтожить проходимый декоратор, попросив построить поверх него здание. Понятное дело, что пока сервер не решит что здание начало строиться, в карте ничего не поменяется.
Примечания:
Все одномерные массивы содержатся в формате: {n, [data]}, а двумерные массивы в виде {x,y,[data]}, где:
Серверу запрещено использовать этот файл, необходимые данные для работы сервера, содержащиеся в этом файле, дублируются в других местах. Файл содержит двумерный массив структур по 16 бит каждая:
Серверу запрещено заглядывать внутрь этого файла Здесь записана информация по миникарте, в формате, о котором надо спрашивать у Coolaceum'а
Формат файла может меняться. На данный момент это текстовый файл, который выглядит так:
находится в разработке
Файл содержит массив полных данных по вершинам ландшафта в его шаблонном состоянии. Раз прочитанный сервером, этот файл перестаёт быть нужным, и сервер переносит работу на свой файл базы данных ландшафта “server.state.landscape.binary”. Структура, описывающая одну вершину такова:
файл является базой данных по карте, в версии сервера. Видимо, содержит данные, эквивалентные “server.const.landscape.binary”. Антон Георгич, Вам слово!
Является компрессированной копией файла “client.const.landscape.binary”. Пофвился ввиду того, что текущий техпроцесс на сервере предпочитает сохранять монополию на упаковку файлов. Ожидаются незначительные потери производительности, так как файл должен компрессироваться и готовиться к раздаче один раз, при старте сервера, и накладные расходы не должны проявлятся во время реальной работы сервера.
Является заменой старого statics.z, то есть содержит в читаемом клиентом виде динамическую часть вершинной информации карты. Забота сервера создать этот файл, используя его, сервера, динамические данные. Файл является двумерным массивом структур:
Свежесгенерированная карта ещё не готова к употреблению: она нуждается в следах работы игровой логики. Если угодно, в “обкатке”. Занимается этим сервер, так как только он должен иметь в себе игровой код. Загрузив карту в необкатанном, то есть шаблонном состоянии, сервер выполняет следуюший алгоритм:
1-2-3-4-5-6-7-0 модификаторы (трава, снег, …)
Таблица значений модификатора (клиент)
q-w-e-r-t-y-u - материал
8-9 поднять-опустить вертекс ландшафту
Описание карты. не помещающееся в регулярную структуру (вертексы).