May 5

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://, можно легко встроить вредоносный код. Более чем реально сделать проверки (могу посоветовать плагин), но это уже отдельная тема, не для этой статьи.

Спасибо!