Хранение и использование игровых данных
Суть
В любой программе мы так или иначе вынуждены хранить информацию, которая будет известна на момент запуска. Например: система диалогов и игре, либо параметры для генерации персонажа, уровня ещё чего-нибудь. Некоторые вещи можно сунуть в монобех и хранить в префабе (урон, скорость, коллайдер, системы объекта). Однако в классах храняться, в основном те вещи, которые не будут подвержены частым изменениям. Т.е. мы единожды настроили свойства игрока - и больше туда не суёмся, пока не заведутся новые механики. Но как быть с параметрами, которые напрямую зависят от гейм дизайна? Скилы, сложность уровня, диалоги, параметры генерации/спавна врагов и т.п. Вещи, которые используют ровно одну и туже механику, но отличаются своими свойствами. Есть вариант рассовать это в префабы и делать для каждой штуки свой префаб. Это имеет право на жизнь и в принципе даже будет работать, правда не очень удобно, когда проект разрастётся, но можно.
Шоделадь?
Велосипед уже придуман, даже не один. Это называют "справочниками" (descriptions) и в том или ином виде исповедуют ровно один подход: блок (совокупность блоков) параметров, на основании которых работает логика упаковывается в отдельный файл. Самый логичный (следствие из названия) способ - использовать json или аналогичный формат данных, где текст будет разбит на блоки и при парсинге файла выковыриваться для использования. У этого есть один неприятный минус: наглядность (вернее ненаглядность :D ). Чтобы с этим работать - надо иметь определённый опыт. Держать в голове простыни текста - занятие не для всех. Плюс всё в жисонке это текст, поэтому есть возможность поломать всё, что хочешь... если руки из жопки
По моему убеждению - работать с текстовыми справочниками, если ты не делаешь этого постоянно неудобно, они быстро превращаются в длинные портянки, где довольно сложно ориентироваться в зависимостях, да и отсутствие шаблонов заставляет табаться постоянно и копипастить кучу сервисного текста.
Альтернатива
В юнити есть сервисный класс ScriptableObject. Который позволяет создавать объекты, заполняемые в инспекторе и использовать их в качестве мини баз данных. Туда можно поместить почти всё, что угодно. Все стандартные типы и кастомные классы могут быть использованы в качестве полей. Более того, как и любой класс, SOшка может реализовывать логику внутри себя, что позволяет реализовать гибкость проекта и легкость его расширения.
Как это работает
Всё начинается с написания класса SO, который является шаблоном для наших объектов, которые мы сможем использовать, как душе угодно. Класс должен быть отнаследован от ScriptableObject и иметь поля, которые мы хотим заполнять. Например - для хранения всего текста в проекте. Реализация выше предоставит нам возможность создать объект текстового поля:
по порядку: CreateAssetMenu необходим для создания экземпляра этого класса в проекте (тупо создать объект, куда вписать текст)
а внутри только поля, которые хочется использовать, как на примере:
энам для определения языка текста (не он определяет язык внутри написания, а пользователь обозначает каким языком написан текст)
публичный стринг для основного текста
и ещё один для дополнительного текста, который можно использовать при доп условиях
Как я говорил в начале - SOшка позволяет реализовывать логику внутри себя, поэтому ничто не мешает, например, присовокупить дополнительный текст к основному или ещё как-то модифицировать поля, в зависимости от потребности системы, например так:
Таким образом можно заложить основу для переводов: создавать для каждого языка в системе соответствующие текстовые поля, которые будут различаться только энамом принадлежности к языку и непосредственно текстом. А для использования этого всего - написать ещё один SO, которые будет хранить для нас версии для всех языков:
в нем же можно, например, хранить и следующий диалог (или несколько и выбирать в зависимости от условий)
например так. Скажем где-то в системе мы храним текущее состояние нашей игры (которое зависит от совершаемых выборов). Для простоты я обозначил это целым числом, но по факту это может быть сложная структура данных, которая хранит в себе все выборы, который делал игрок за всё время проходжения игры. И на основании этого состояния определяем который из диалогов передать в систему. Список диалогов мы определяем сами, складывая в массив в инспекторе (в данном примере позиция в массиве завязана на состояние, но ничто не мешает нам ввести ещё один энам, например такой:
внести его в сам хэндлер и определять какой именно стейт в нем обозначен текстом. Немного меняем код, чтобы метод возвращал нам именно тет объект, который будет соответствовать текущему состоянию прогресса:
Вот в общем-то и всё. Осталось только вывести этот текст в поле UI и радоваться.
пишем новый монобех, накидываем на нужный элемент канваса, добавляем кнопки, связываем кнопки с нашим монобехом через onClic() в который пропихиваем нужный метод
а вот и публичный метод, который прокидываем в onClick. Он появится там сам (там будут все публичные методы для этого объекта). Можно было сразу на метод отрисовки текста дать ссылку, но вдруг мы захотим делать ещё какие-то вещи или (что вероятнее) захотим отрисовывать диалог не только по этому событию.
собственно всё.
Когда же нам захочется добавить новый язык - мы сможем расширить энам и добавить объекты для нового языка. Да, есть недостаток в этом действе относительно json файла, который можно было бы взять и промтом перевести. Но, обычно всем хочется художественные переводы, а значит так или иначе придётся текст обрабатывать. А так как SO это объекты эдитора - никто не мешает нам написать обработчик, который возьмет файл с текстом, распарсит его и на его основании создаст необходимое количество объектов с необходимыми полями. В чем, я считаю, неоспоримое достоинство SO - так это возможность реализовать логику для возвращаемых объектов, а так же большая наглядность в хранении информации. Плюс явная связность ресурсов в проекте, что позволяет быстро находить баги и не держать в голове миллионы названий ресурсов.
З.ы. этим же инструментарием можно пользоваться для хранения информации генераторов. Например создавать экземпляр врага, навешивая на него только те модули логики, которые ему нужны.
Например SOшка генерации уровня из моей прошлой темы:
номер уровня, позиция игрока, начальной платформы, позиция для рождения скила. Тип скила, который я хочу на этот уровень закинуть. Лист объектов, из которых будет собран уровнеь. И количество частей из которого будет состоять уровень. Соответственно можно сделать новый уровень просто перетасовав значения в SO.
Лига Разработчиков Видеоигр
7.8K постов22.7K подписчика
Правила сообщества
ОБЩИЕ ПРАВИЛА:
- Уважайте чужой труд и используйте конструктивную критику
- Не занимайтесь саморекламой, пишите качественные и интересные посты
- Никакой политики
СТОИТ ПУБЛИКОВАТЬ:
- Посты о Вашей игре с историей её разработки и описанием полученного опыта
- Обучающие материалы, туториалы
- Интервью с опытными разработчиками
- Анонсы бесплатных мероприятий для разработчиков и истории их посещения;
- Ваши работы, если Вы художник/композитор и хотите поделиться ими на безвозмездной основе
НЕ СТОИТ ПУБЛИКОВАТЬ:
- Посты, содержащие только вопрос или просьбу помочь
- Посты, содержащие только идею игры
- Посты, единственная цель которых - набор команды для разработки игры
- Посты, не относящиеся к тематике сообщества
Подобные посты по решению администрации могут быть перемещены из сообщества в общую ленту.
ЗАПРЕЩЕНО:
- Публиковать бессодержательные посты с рекламой Вашего проекта (см. следующий пункт), а также все прочие посты, содержащие рекламу/рекламные интеграции
- Выдавать чужой труд за свой
Подобные посты будут перемещены из сообщества в общую ленту, а их авторы по решению администрации могут быть внесены в игнор-лист сообщества.
О РАЗМЕЩЕНИИ ССЫЛОК:
Ссылка на сторонний ресурс, связанный с игрой, допускается только при следующих условиях:
- Пост должен быть содержательным и интересным для пользователей, нести пользу для сообщества
- Ссылка должна размещаться непосредственно в начале или конце поста и только один раз
- Cсылка размещается в формате: "Страница игры в Steam: URL"