wiki:WorkingWithTilesets_v1

Version 2 (modified by sagrer, 16 years ago) (diff)

--

Принцип работы с наборами тайлов (тайлсетами)

Версия №1. В процессе проектирования.

Более старые версии:

  • нет.

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


Организация файлов тайлсетов

Все тайлсеты расположены в одном каталоге. Внутри этого каталога - по подкаталогу на каждый тайлсет, имя каталога является именем тайлсета. Внутри каждого каталога тайлсета содержатся следующие файлы:

  • TileSet.ini - настройки тайлсета. Формат файла описан тут.
  • Если набор не сжатый - то куча *.bmp-файлов с тайлами с именем вида {TileName}.bmp, где {TileName}=T_{Layer}_{Terrains}_{VarNumber}, где {Layer} - двузначное число, номер слоя (любое число от единицы до 99, ибо номер слоя 00 зарезервирован); {VarNumber} - двузначное число, номер варианта данного тайла; {Terrains}={Terr1}_{Terr2}_{Terr3}_{Terr4}, где {Terr1}-{Terr4} соотв названия террейнов в углах тайла - по строчкам, справа-налево, сверху-вниз, вот так:
Terr1Terr2
Terr3Terr4
  • Если набор сжатый - куча *.7z-файлов с bmp-хами с тайлами с именем вида {TileName}.bmp, где {TileName} - то же самое, что описано выше.
  • Пустые файлы с именами вида Deny_{Terr1}_{Terr2} где Terr1 и Terr2 - имена террейнов, между которыми запрещены переходы. Именем террейна может быть зарезервированное имя All что означает все террейны того же слоя что и второй террейн. По-умолчанию все переходы разрешены. Наличие такого файла запрещает переход. Порядок следования террейнов в имени значения не имеет. Смысловые дубли не должны мешать правильной работе реализации, но в то же время реализация должна стараться не допускать появления таких дублей, а также отслеживать их и при обнаружении вычищать. Имена Deny имеют более высокий приоритет над разрешением всех переходов по умолчанию и более низкий к именам Allow, поэтому можно сначала запретить все переходы для какого-либо террейна, а затем уже разрешать определенные переходы через Allow. Писать Deny_All_All нельзя, такой файл если он появится - должен быть удален, ибо настройки переходов в реализации должны идти для каждого террейна отдельно.
  • Пустые файлы с именами вида Allow_{Terr1}_{Terr2} где Terr1 и Terr2 - имена террейнов, между которыми разрешены переходы. Использование имени All в данном случае бессмысленно, ибо по умолчанию все переходы уже разрешены. Отношение к дублям - такое же как для Deny.

Принцип реализации

Работа с тайлами реализуется несколькими взаимозависимыми классами, связанными через их элементы-ссылки. Основным классом, связывающим все остальные является TTileSet, который реализует работу с содержимым одного набора тайлов. Есть более высокий уровень - TTileSetsCollection - фактически контейнер, содержащий ссылки на объекты типа TTileSet для всех доступных программе тайлсетов. TTileSetsCollection содержит:

  • Массив ссылок на все объекты тайлсетов.
  • Путь по которому располагаются тайлсеты.
  • Путь к временному каталогу для временных файлов при работе с тайлсетами.
  • Ссылка на CallBack-метод, для синхронизации при многопоточной работе, с учетом того что CallBack будет иметь доступ к инфе объекта и всей вложенной инфе.

Также содержит методы:

  • Удалений, добавлений тайлсетов.
  • Методы, осуществляющие собственно чтение тайлсетов. Предусмотреть основной метод, выполняющий всю работу и своевременные вызовы CallBack, а также открытые методы-обертки для вызова с и без использования CallBack, при этом основной метод можно спрятать от простых смертных и оставить доступными только обертки.
  • Метод, возвращающий true если в каком-либо из тайлсетов есть "неправильные" (с нестандартной структурой) тайлы и false если таких нет.

TTileSet содержит:

  • Обратную ссылку на объект TTileSetsCollection.
  • Ссылку на объект, реализующий работу с файлом TileSet.ini для этого тайлсета (TTileSetIni).
  • Массив ссылок на все тайлы, принадлежащие тайлсету - точнее на объекты класса TTile, реализующего работу с тайлом.
  • Массив ссылок на все тайлы, файлы которых реально существуют.
  • Массив ссылок на все тайлы, конфигурация которых неправильна (т.е. является одним из вариантов поворота для одной из стандартных конфигураций, а значит такой тайл требуется перевернуть в стандартную конфигурацию при нулевом повороте и переименовать).
  • Массив ссылок на все террейны, принадлежащие тайлсету - т.е. на объекты класса TTerrain, реализующего работу с террейном.
  • Массив ссылок на объекты класса TLayer, который по-сути является контейнером ссылок на объекты TTerrain и содержит ссылки на все террейны своего слоя.
  • Список строк Allow и Deny для информации из имен файлов разрешений-запретов переходов.
  • Дополнительная информация о тайлсете, например номер ревизии в случае если используется svn.
  • Объект класса TFlistFormat для списка тайлов в сжатом тайлсете.
  • Объект класса TFlistFormat для списка тайлов в временном каталоге с тайлами тайлсета. Последние 2 элемента - используются только если работаем со сжатым тайлсетом.
  • Список строк - имена всех террейнов тайлсета, мне выполнения методов должен быть в алфавитном порядке и соответствовать приоритету террейнов.
  • Булево значение - true если есть "неправильные" тайлы и false если таковых нет.
  • Булево значение - true если есть тайлы без реального файла с картинкой и false если таковых нет.
  • Ссылка на объект класса TMasksSet - описание класса смотри на странице? с описанием работы масок.

Также тут есть методы для:

  • Удалений, добавлений тайлов (по имени, влияющая на все три массива ссылок на тайлы).
  • Удалений, добавлений террейнов (по имени).
  • Удалений, добавлений слоев (по номеру слоя, с удалением принадлежащий слою террейнов).
  • Удалений, добавлений Allow и Deny, влияющее и на соответствующие объекты террейнов.
  • Метод для синхронизации временного распакованного варианта тайлсета со сжатым реальным тайлсетом.
  • Метод для обратной синхронизации содержимого временного каталога со сжатым реальным тайлсетом.
  • Метод, собственно выполняющий чтение информации о тайлсете.
  • Метод, ищущий неправильные тайлы, добавляющий их в соотв. списки, помечающий и выставляющий соотв. булево значение о их наличии в данном объекте.
  • Метод, генерящий на основе информации о слое и переходах террейна конфигурации всех возможных для террейна тайлов, проверяющий наличие каждого такого тайла, если его нету - добавляющий "несуществующий" (т.е. для него отсутствует файл с картинкой) тайл в систему и все нужные списки, помечающий правильным образом этот тайл и ставящий в нужную метку соотв. булево значение этого объекта о наличии "несуществующих" тайлов.
  • Метод, исправляющий тайлы с неправильной структурой (поворачивает и дает правильное имя).
  • Метод, осуществляющий генерацию конкретного переходного тайла (в аргументе линка на объект тайла).
  • Метод, осуществляющий генерацию всех переходных тайлов, для которых еще нету картинок.

Удаление тайлов, разрешений\запретов переходов, если оно связано с реальным файлом набора - автоматически приводит к удалению соответствующих файлов из набора. Юзверь этой системы классов должен сам обеспечить предупреждение пользователя проги о возможном безвозвратном удалении файлов до вызова таких методов. Кроме этого - следует организовать выделение и освобождение памяти так, чтобы памятью всех слоев, террейнов и тайлов заведовал именно класс TTileSet. Другие классы также могут иметь подобные ссылки, и даже выделять память под новые объекты - но в любом случае такой объект должен попасть в массив объекта TTileSet и в дальнейшем уничтожаться должен под управлением именно TTileSet, если же вложенные в него объекты сами новых объектов не создают, то использоваться может только копирование ссылки на уже созданный объект. Таким образом, в других классах может использоваться new, но delete всегда выполняется только классом TTileSet, причем гарантированно для всего, что могло быть создано по любому такому new.

Класс TLayer весьма прост и содержит только номер слоя (в виде числового значения), массив ссылок на террейны и обратную ссылку на тайлсет, которому принадлежит слой.

Класс TTileSetIni по сути также более чем прост, типичен для любого класса, работающего с конфигами в ini, содержит в себе структуры и переменные с данными секций TileSet.ini, а также методы Load, Save, Clean для загрузки, чтения информации в\из файла и очистки (простановке значений по умолчанию) соответственно. Метод Load должен проверять соответствие номера версии формата в файле текущей реализации - если в файле версия новее - давать ошибку, если старее - конвертировать файл в новую версию (без записи на диск), если тот же - просто читать.

На уровне террейна работа обеспечивается объектами класса TTerrain, который содержит:

  • Имя террейна.
  • Число - приоритет террейна. Если приоритет неизвестен то значение = 0.
  • Ссылку на объект класса TLayer, которому принадлежит террейн. Если имеются проблемы с идентификацией единственного номера слоя - присваивается ссылка на слой с индексом 00 (ноль).
  • Численное значение слоя террейна. Если это 00 значит имеются проблемы с идентификацией номера слоя (например есть тайлы для одного террейна и разных слоев).
  • Массив чисел со всеми найденными для этого террейна значениями слоя.
  • Списки Allow и Deny содержащие имена террейнов, на которые, соответственно, разрешен или запрещен переход.
  • Массив ссылок на все тайлы террейна.
  • Обратная ссылка на тайлсет, которому принадлежит террейн.

Также тут есть методы для:

  • Удаления\добавления тайлов. Работают по имени или индексу. Убеждаются что такой тайл имеется в террейне, затем удаляется на уровне тайлсета.
  • Удаления\добавления элементов Allow и Deny - с синхронизацией с вовлеченными террейнами и списками реальных имен файлов в объекте тайлсета.
  • Смена слоя. Работает независимо от того, есть ли проблемы со слоями, меняет слой, переименовывает файлы тех тайлов, от которых есть какой-то смысл, остальные тайлы (т.е. те переходы, которые больше не нужны) либо удаляются, либо переименовываются как будто бы в них был смысл (в зависимости от аргументов метода) - второй вариант может использоваться при смене слоя сразу для нескольких террейнов без потери перехода между ними. При наличии проблемы со слоями, даже если работает с удалением

На уровне тайла вся работа обеспечивается классом TTile, который содержит:

  • Ссылку на свой тайлсет.
  • Ссылку на свой слой.
  • Путь к bmp-файлу, с именем файла (строка).
  • Путь к файлу в тайлсете, с именем файла (может не совпадать с bmp если оно например 7z).
  • Ссылка на объект типа TTileTerrs - содержит информацию о структуре тайла (территории) в удобном для быстрой программной обработки виде.
  • Номер варианта тайла с такой структурой.
  • Булевое значение Exists - true если тайл реально существует в тайлсете а не только должен наличествовать в теории исходя из структуры террейнов на слоях и возможных переходов.
  • Булевое значение IsValid - true если структура тайла соответствует одной из стандартных, если соответствия нет - тайл является одним из вариантов поворота для одной из стандартных конфигураций, а значит такой тайл требуется перевернуть в стандартную конфигурацию при нулевом повороте и переименовать.

Также тут есть методы для:

  • Загрузки информации о тайле по его имени (смотрит свойства тайлсета и ищет нужные файлы).
  • Метод, возвращающий строку с именем тайла.
  • Метод, возвращающий повернутую нужное количество раз bmp-ху тайла.
  • Метод, возвращающий bmp-ху тайла без поворотов (вызывает предыдущий метод с нулевым поворотом).

Класс TTileTerrs был бы простой структурой если бы не имел методы сравнения двух объектов этого типа, поэтому он класс. Собержит:

  • Строки A,B,C,D с именами террейнов в соотв. углах (справа налево, по строкам сверху вниз, A соответствует {Terr1} а D соответствует {Terr4} в имени тайла). Вот табличка расположения углов:
AB
CD
  • Ссылка на родной тайл.

Также содержит методы:

  • Для чтения террейнов в углах (т.е. ABCD) из имени-строки тайла (и записи в инфу объекта).
  • Для генерации имени тайла по ABCD без той части где указывается вариант тайла.
  • Метод, возвращающий integer - если аргумент не совпадает по структуре с тайлом (аргумент также типа TTileTerrs) - вернет -1, иначе - вернет количество поворотов которое нужно применить к аргументу чтобы структура тайла совпадала со структурой аргумента. Таким образом если возвращает 0 значит структура тайла и аргумента метода идентичны.
  • Перегрузка оператора == - при сравнении двух объектов этого типа этим оператором - соотв. возвращает true если объекты идентичны и false если чем-либо различаются.

Как определяется правильна ли структура тайла

Для начала определимся с приоритетом террейнов. Приоритет определяется по имени террейна, в алфавитном порядке (не только для первой, для всех букв имени террейна). Чем выше в списке по алфавиту идет имя террейна тем выше его приоритет. В дальнейшем буду считать что террейн с наивысшим приоритетом имеет номер 1, террейн с приоритетом ниже номер 2, ниже номер 3 и так далее. На основе этого допущения определим стандартные структуры террейнов:

  • Из двух террейнов-1:
22
12
  • Из двух террейнов-2
11
21
  • Из двух террейнов-3:
11
22
  • Из трех террейнов-1:
11
23
  • Из трех террейнов-2:
11
32
  • Из трех террейнов-3:
12
31
  • Из трех террейнов-4:
22
13
  • Из трех террейнов-5:
22
31
  • Из трех террейнов-6:
33
12
  • Из трех террейнов-7:
33
21
  • Из четырех террейнов-1:
12
34
  • Из четырех террейнов-2:
12
43
  • Из четырех террейнов-3:
13
24
  • Из четырех террейнов-4:
13
42
  • Из четырех террейнов-5:
14
23
  • Из четырех террейнов-6:
14
32

Как это все по идее должно работать

В начале программа создает верхний уровень - объект класса TTileSetsCollection. Все остальные уровни создаются конструкторами и прочими методами этого объекта и его вложенными объектами. Освобождение памяти во избежание утечек - там же, деструкторами классов и их методами. Программа должна позаботиться только о правильном удалении объекта TTileSetsCollection когда он станет не нужен.

После создания объекта класса TTileSetsCollection - программа пробивает в его опции все необходимые пути, если нужно - CallBack-метод, а затем вызывает нужный метод чтения информации тайлсетов.

Данный метод - читает имена тайлсетов, их количество - и создает нужное количество подобъектов класса TTileSet. Обращаю внимание, что все вложенные в TTileSetsCollection объекты имеют обратные ссылки на "родительский" объект, т.е. тот объект в который они вложены - что позволяет этим объектам использовать информацию своего владельца и даже своих соседей и вложенных в соседей объектов.

Для каждого объекта TTileSet вызывается метод чтения информации тайлсета. Причем перед запуском чтения тайлсета при необходимости вызывается CallBack что позволяет индицировать процесс загрузки, например, в окошко с прогрессбаром.

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

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

Затем осуществляется генерация всех возможных тайлов-переходов - если такой переходный тайл уже наличествует, хотя-бы в количестве 1 (адЫн) штука - ничего не происходит, если же ни одного такого тайла найдено не было - создается объект тайла, с пометкой что файла картинки у него нет, ссылка на него добавляется во все соответствующие массивы соответствующих объектов.

Собсно это все что касается механизма чтения информации о тайлсете. После его завершения - в объекте класса TTileSetsCollection содержится вся необходимая информация о всех имеющихся тайлсетов, более чем достаточная для вывода любой информации в интерфейс программы, в т.ч. дерева террейнов и тайлов.

Кроме этого у всех объектов системы имеются дополнительные методы, которые не вызываются при чтении тайлсетов, и предназначены для непосредственного их вызова программой - например методы удаления\добавления тайлов, генерации переходных тайлов, исправления "неправильных" тайлов и т.д. Подробно расписывать эти методы смысле нет, ибо механизм их действия понятен их простого их описания, а описания же для них наличествуют выше в данном тексте, в списках методов классов.

Спасибо за то что прочитали весь этот бред :).