Управляемое данными кодирование
Каждая вещь, которую вы упоминаете, может быть указана в данных. Почему ты грузишь aspecificmap
? Потому что конфигурация игры говорит, что это первый уровень, когда игрок начинает новую игру, или потому что это имя текущей точки сохранения в файле сохранения игрока, который он только что загрузил, и т. Д.
Как ты находишь aspecificmap
? Потому что он находится в файле данных, в котором перечислены идентификаторы карт и их ресурсы на диске.
Должен быть только очень небольшой набор «основных» ресурсов, которые по закону трудно или невозможно избежать жесткого кодирования. Немного поработав, это может быть ограничено одним жестко заданным именем актива по умолчанию, например main.wad
или тому подобным. Этот файл потенциально может быть изменен во время выполнения путем передачи аргумента командной строки в игру, иначе game.exe -wad mymain.wad
.
Написание кода, управляемого данными, основывается на нескольких других принципах. Например, можно избежать того, чтобы системы или модули запрашивали конкретный ресурс и вместо этого инвертировали эти зависимости. То есть не DebugDrawer
загружайте debug.font
его в код инициализации; вместо этого нужно DebugDrawer
взять дескриптор ресурса в своем коде инициализации. Этот дескриптор может быть загружен из основного файла конфигурации игры.
В качестве конкретных примеров из нашей кодовой базы у нас есть объект «глобальных данных», который загружается из базы данных ресурсов (которая сама по умолчанию является ./resources
папкой, но может быть перегружена аргументом командной строки). Идентификатор базы данных ресурсов этих глобальных данных является единственным необходимым жестко запрограммированным именем ресурса в кодовой базе (у нас есть другие, потому что иногда программисты становятся ленивыми, но мы обычно в конечном итоге исправляем / удаляем их). Этот глобальный объект данных полон компонентов, единственной целью которых является предоставление данных конфигурации. Одним из компонентов является компонент глобальных данных пользовательского интерфейса, который содержит дескрипторы ресурсов для всех основных ресурсов пользовательского интерфейса (шрифты, файлы Flash, значки, данные о локализации и т. Д.), А также ряд других элементов конфигурации. Когда разработчик UI решает переименовать главный актив UI от /ui/mainmenu.swf
до/ui/lobby.swf
они просто обновляют эту глобальную ссылку на данные; код двигателя не нужно менять вообще.
Мы используем эти глобальные данные для всего. Все игровые персонажи, все уровни, пользовательский интерфейс, аудио, основные ресурсы, конфигурация сети, все. (ну, не все , но эти другие ошибки исправляются.)
Этот подход имеет много других преимуществ. С одной стороны, это делает упаковку и объединение ресурсов неотъемлемой частью всего процесса. Твердо заданные пути в движке также означают, что эти же пути должны быть жестко закодированы в любых сценариях или инструментах, упаковывающих игровые ресурсы, и эти пути затем могут быть не синхронизированы. Вместо этого, опираясь на один основной актив и цепочки ссылок, мы можем создать пакет активов с помощью одной команды, например, bundle.exe -root config.data -out main.wad
и знать, что она будет включать все необходимые нам активы. Кроме того, поскольку упаковщик будет просто следовать ссылкам на ресурсы, мы знаем, что он будет включать только те ресурсы, которые нам требуются, и пропустит весь оставшийся пух, который неизбежно накапливается в течение жизни проекта (плюс мы можем автоматически создавать списки этих ресурсов). пух для обрезки).
Хитрый угловой случай всего этого в сценариях. Концептуально создать движок, управляемый данными, легко, но я видел очень много проектов (хобби для AAA), где сценарии считаются данными и, следовательно, «разрешено» просто использовать пути к ресурсам без разбора. Не делай этого. Если файлу Lua нужен ресурс, и он просто вызывает такую функцию, как textures.lua("/path/to/texture.png")
у конвейера ресурсов, будет много проблем, зная, что скрипт /path/to/texture.png
должен работать правильно, и он может счесть эту текстуру неиспользованной и ненужной. Сценарии должны обрабатываться как любой другой код: любые данные, которые им нужны, включая ресурсы или таблицы, должны быть указаны в записи конфигурации, которую механизм и конвейер ресурсов могут проверять на наличие зависимостей. Данные, которые говорят "загрузить скрипт foo.lua
", вместо этого должны сказать "foo.lua
и задайте ему эти параметры ", где параметры включают любые необходимые ресурсы. Если сценарий случайным образом порождает врагов, например, передайте список возможных врагов в сценарий из этого файла конфигурации. Затем механизм может предварительно загрузить врагов с уровнем ( так как он знает полный список возможных порождений) и конвейер ресурсов знает, как связать всех врагов с игрой (так как на них окончательно ссылаются данные конфигурации). Если сценарии генерируют строки с именами путей и просто вызывают load
функцию, то ни движок и конвейер ресурсов не могут каким-либо образом точно знать, какие ресурсы может попытаться загрузить скрипт.
Точно так же вы избегаете жесткого кодирования в общих функциях.
Вы передаете параметры и сохраняете свою информацию в конфигурационных файлах.
В этой ситуации нет абсолютно никакой разницы в разработке программного обеспечения между написанием движка и написанием класса.
Затем ваш клиентский код читает «главный» файл конфигурации ( этот файл либо жестко запрограммирован, либо передается в качестве аргумента командной строки), который содержит информацию, которая сообщает, где находятся файлы ресурсов и какую карту они содержат.
Оттуда все управляется "главным" файлом конфигурации.
источник
Мне нравятся другие ответы, поэтому я буду немного противоречить. ;)
Вы не можете избежать кодирования знаний о ваших данных в ваш движок. Откуда исходит информация, двигатель должен знать, чтобы искать ее. Тем не менее, вы можете избежать кодирования самой фактической информации в ваш движок.
«Чистый» подход, основанный на данных, позволил бы запустить исполняемый файл с параметрами (параметрами) командной строки, необходимыми для загрузки исходной конфигурации, но для того, чтобы понять, как интерпретировать эту информацию, необходимо будет кодировать механизм. Например , если ваши файлы конфигурации в формате JSON, вы должны жестко закодировать переменные , искать, например , двигатель должен знать , искать
"intro_movies"
и"level_list"
и так далее.Тем не менее, «хорошо сконструированный» движок может работать для многих различных игр, просто обмениваясь данными конфигурации и данными, на которые он ссылается.
Таким образом, мантра не столько избегает жесткого кодирования, сколько гарантирует, что вы можете вносить изменения с наименьшими усилиями.
В отличие от подхода к файлам данных (который я искренне поддерживаю), может случиться так, что вы можете компилировать данные в свой движок. Если «стоимость» этого ниже, тогда нет никакого реального вреда; если вы работаете над этим только тогда, вы можете отложить обработку файла на более поздний срок и не обязательно испортить себя. Мои первые несколько игровых проектов имели большие таблицы данных, жестко запрограммированные в самой игре, например, список оружия и их различные данные:
Таким образом, вы помещаете эти данные куда-то, чтобы их можно было легко ссылаться, и их легко редактировать по мере необходимости. Идеальным было бы поместить этот материал в какой-то конфигурационный файл, но тогда вам нужно выполнить синтаксический анализ и перевод, и весь этот джаз, плюс добавление межструктурных ссылок может стать дополнительной болью, которую вы действительно не хотите иметь дело с.
источник