August 9, 2018

Немного о классах в Godot 3.1

Не так давно в Годо была добавлена возможность создавать свои ноды одной строкой (а не только плагинами).

Теперь можно например в скрипте игрока написать class_name Player и где-нибудь в другом месте получить экземпляр этого класса через Player.new().

Всё вроде бы хорошо, давайте далее.

А дальше камней только больше. Я задался задачей создания экземпляра, имея имя класса в виде строки (например вытянутой из конфига или базы данных). Немного поисков (а точнее достаточно много, раз я пишу это) и я обнаруживаю класс ClassDB, который хранит в себе все доступные пользователю классы движка. Да ещё и есть метод instance(), в который можно передать имя класса в виде строки и получить обратно экземпляр класса.

Начинаем разбираться в проблеме.

Победа? Не совсем. Мы можем написать например ClassDB.instance('Node2D') и в ответ получить наш класс Node2D, на если мы напишем ClassDB.instance('Node2D'), то получим Null. Дело в том, что ClassDB хранит в себе только классы движка, а классы, созданные пользователем не попадают туда.

Если проблема и есть, то она где-то рядом. Идем в репозиторий Годо на GitHub и ищем как работает наш class_name. После недолгих поисков я обнаружил, что пользовательские классы - не совсем классы, но всё же классы, потому что скрипт - это класс.

В общем я быстро поглядел на код и увидел, что пользовательские классы называются global_script_class. Уже тепло. Теперь открываем документацию и пытаемся отыскать что-то подобное и обнаруживаем, что у нас есть доступ к ProjectSettings._global_script_classes (вероятно это не правильно. Не зря же там _ в начале имени переменной). Принтуем это и получаем что-то вроде [(base:Area2D), (class:Chest), (language:GDScript), (path:res://chest.gd), (base:Sprite), (class:Didon), (language:GDScript), (path:res://spidt.gd)]. Зрелище не очень, но если вывести это немного иначе, то получим весьма читаемое чудо:

Теперь мы видим, что это массив, где в каждой ячейке лежит словарь с четырьмя ключами. Таким образом, чтобы получить пользовательский класс нам необходимо создать экземпляр класса, который написал в 'base', и загрузить в него скрипт из 'path'. Немного "улучшим эту идею" и сделаем из этого функцию:

Итого

Данный способ получился немного непредсказуемым, но зато я ещё немного прокачал свой скилл. Теперь об этом знаю не только я, но и ты, читатель.

На самом деле всё началось с того, что Хуан (разработчик Godot) добавил новый класс Expression и я хотел его попробовать, но конструкцию вроде "%s.new() % 'ClassName' " класс конечно переваривал, но на выходе выдавал null и я реши всё так и "докопаться до сути" и решить эту проблему. Вот так вот попытка изучить что-то одно позволила мне изучить что-то другое, но не менее нужное и важное.