Resources в Godot на примере волшебных документов Гринготтса
Всем привет! Это Андрей из "Геймдева Вышки".
Сегодня хочу рассказать вам о Ресурсах (Resources) в Godot. Это крутейший инструмент для организации кода и работы с данными для геймдизайнера. Но всё по порядку.
В этой статье покажу базу: как с ними работать, где их используют чаще всего и какие ошибки стоит избегать. Рассмотрим супер простую аналогию, которая позволит разобраться даже с нулевым опытом.
Волшебные документы в Гринготтсе
Во вселенной Гарри Поттера есть магический банк Гринготтс. Как и полагается банку, в нём хранится очень много похожих и одинаковых документов – кредиты, платёжные поручения, листы ознакомления с чем-либо.
В таких документах нужно только заполнить основную информацию – ФИО, дату, подпись. Поэтому писать такой документ каждый раз с нуля – лишняя работа, а лучше сразу сделать заготовку, чтобы её печатать.
Создание ресурсов
Resources похожи на ScriptableObject из Unity. Они дают способ хранить данные в одном месте в чётком формате.
Ресурсы – это такие же .gd файлы, что и другие скрипты. Разница в том, что ресурсы наследуются от класса Resource, а не от Node, поэтому их нельзя прикрепить к нодам.
В нашей аналогии, скрипт ресурса – это как шаблон документа в Word, который ещё не распечатали.
Такому файлу важно дать название, чтобы отличать от других. В Godot мы присваиваем классу название, чтобы отличать его среди других ресурсов.
Теперь в списке доступных ресурсов появился и наш банковский документ! Можно создать экземпляр, который сохранится на диске. Как будто мы распечатали этот файл.
Теперь в наш документ можно добавить какие-нибудь данные, например название банка. Эта информация одинаковая для всех документов, поэтому можно её добавить в Word файл, а не заполнять вручную. Это будет переменная bank_name.
Вместе с этим можно добавить поля для ввода. Например: ФИО клиента и год подписания.
Так как это такой же .gd файл, что и обычный скрипт, внутри него можно объявлять @export переменные. Их мы и используем.
Работа с ресурсами
Давайте дадим нашим документам какое-нибудь применение. Наймём в наш банк гоблина, который будет читать данные из документа. Потом займём его более полезными делами.
Сейчас гоблин может взять любой ресурс себе в руки. Но если это не банковский документ, то в нём может не быть нужных данных. Поэтому я использую название класса, чтобы гоблин мог брать только важные бумажки.
Логика внутри ресурсов
Мы что-то забыли, что это не просто банк – а Гринготтс! И документы в нём используются не простые, а магические. Давайте добавим "магию" (действие) нашим документам.
Пока что я не добавил какое-либо магическое действие в наш документ. А всё потому, что наша бумажка в принципе не о чём. Она не делает ничего, только хранит инициалы и год. Позже это исправим.
А пока мы можем поручить нашему гоблину применять магическое действие для того документа, который мы ему дадим.
Принципы ООП в ресурсах
В нашем банке много всяких разных документов. Давайте сделаем так, чтобы все они наследовались от BankApprovalDocument. Я создам два новых ресурса: один будет переводом, а другой – пополнением баланса.
Так как они наследуются от BankApprovalDocument, гоблин всё ещё способен работать с ними:
Кроме того, благодаря наследованию в новых типах документов всё ещё есть название банка, данные клиента, а главное – магическое действие.
И мы можем переопределить его, чтобы разные документы делали разные вещи!
@abstract
Наш исходный BankApprovalDocument сам по себе не используется. От него только наследуются действительно полезные бумаги. Поэтому давайте запретим печатать и использовать этот документ с помощью @abstract.
Работает как virtual в других языках программирования. Добавляем ключевое слово перед названием класса и перед каждой функцией, которые должны реализовать наследники.
Теперь гоблин не сможет использовать такие документы:
А во всех классах, где мы не реализовали do_magic_action() будет появляться ошибка:
Как используются ресурсы?
Предметы и метаданные
Я думаю, на примере документов уже понятно, для чего могут использоваться ресурсы. Очень часто их используют для хранения данных предмета без логики поведения: названия, иконки, крафта, уровня для открытия.
Если мы делаем систему инвентаря, то можно написать одну ноду InventoryItem, которая на вход будет получать ресурс с данными и отображать их как предмет. Логика предмета будет храниться в сцене, а данные – в ресурсе.
Геймдизайнеру удобно работать с ресурсами: ему доступны удобные поля, которые можно использовать, чтобы быстро итерировать или полностью менять параметры.
Ноды и ресурсы в Godot очень похожи. Но первые всегда находятся на сцене и выполняют действия, а вторые только хранят данные. Можно всё делать через ноды, но ресурсы позволяют строго отделять данные от логики.
Работа с файлами
С помощью функций ResourceSaver.save() и load() можно легко сохранять ресурсы на диск:
Поэтому их удобно использовать для:
- сохранения прогресса игрока
- хранения пользовательских уровней (можно легко сделать редактор карт)
- создания модов
Но есть нюанс с безопасностью. В Godot ресурсы, которые находятся в папке user://, можно легко встроить вредоносный код. Более чем реально сделать проверки (могу посоветовать плагин), но это уже отдельная тема, не для этой статьи.