Класс Object

См. также

На этой странице описывается реализация объектов в Godot на C++. Ищете справочник по классу Object? Посмотрите здесь.

Общее определение

Object — базовый класс практически для всего. Большинство классов в Godot наследуются от него напрямую или косвенно. Объекты предоставляют рефлексию и редактируемые свойства, а их объявление осуществляется с помощью одного макроса, например:

class CustomObject : public Object {

    GDCLASS(CustomObject, Object); // this is required to inherit
};

Это значительно расширяет функциональность Objects. Например:

obj = memnew(CustomObject);
print_line("Object class: ", obj->get_class()); // print object class

obj2 = Object::cast_to<OtherClass>(obj); // converting between classes, this also works without RTTI enabled.

Источники:

Регистрация Object

ClassDB это статический класс который хранит полный лист зарегистрированных классов наследуемых от Object, и динамические связки для всех их методов свойств и целочисленных констант.

Классы регистрируются через вызов:

ClassDB::register_class<MyCustomClass>()

Регистрация позволяет классу быть инстанцируемым из скриптов, кода, или созданием их снова при десериализации.

Регистрация как виртуальный такое же но не может быть инстанцируемо.

ClassDB::register_virtual_class<MyCustomClass>()

Классы наследника Object могут быть перезаписаны в статической функции static void _bind_methods(). Когда один из классов регистрируется, эта статическая функция вызывается для регистрации все его методов, свойств, констант, итд. Она вызывается лишь единожды. Если класс наследник Object инстанцируется не будучи зарегистрированным, он будет автоматически зарегистрирован как виртуальный.

Внутри _bind_methods, есть целая куча вещей которые могут быть сделаны. Регистрация функций одна из них:

ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method);

Значения аргументов по умолчанию могут быть переданы как параметры в конце:

ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method, DEFVAL(-1), DEFVAL(-2)); // Default values for arg2name (-1) and arg3name (-2).

Значения по умолчанию должны быть указаны в том же порядке, в котором они объявлены, пропуская обязательные аргументы и указывая значения по умолчанию для необязательных. Это соответствует синтаксису объявления методов в C++.

D_METHOD это макрос для конвертации "methodname" в StringName для большей эффективности. Имена аргументов используются для интроспекции, но во время компиляции для релиза, макрос игнорирует их, так что эти строки будут не использованы и выброшены при оптимизации.

Смотрите _bind_methods для Control или Object для подробных примеров.

Если просто добавить модули и функциональность которую не нужно добавлять в документацию, макрос D_METHOD() можно спокойно проигнорировать и строка с именем может быть использована для краткости.

Источники:

Константы

Классы часто содержат перечисления такие как:

enum SomeMode {
   MODE_FIRST,
   MODE_SECOND
};

Чтобы они работали при привязке к методам, перечисление должно быть объявлено преобразуемым в int. Для этого предусмотрен макрос:

VARIANT_ENUM_CAST(MyClass::SomeMode); // now functions that take SomeMode can be bound.

Константы также можно привязывать внутри _bind_methods, используя:

BIND_CONSTANT(MODE_FIRST);
BIND_CONSTANT(MODE_SECOND);

Свойства (set/get)

Объекты экспортируют свойства, которые полезны для следующего:

  • Сериализации и десирализации объекта.

  • Создание списка редактируемых значений для наследуемых от Object классов.

Свойства обычно определяются классом PropertyInfo() и конструируются как:

PropertyInfo(type, name, hint, hint_string, usage_flags)

Например:

PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "0,49,1", PROPERTY_USAGE_EDITOR)

Это целочисленное свойство с именем "amount". Подсказка представляет собой диапазон от 0 до 49 с шагом 1 (целые числа). Его можно использовать только в редакторе (визуальном редактировании значения), но он не будет сериализован.

Другой пример:

PropertyInfo(Variant::STRING, "modes", PROPERTY_HINT_ENUM, "Enabled,Disabled,Turbo")

Это строковое свойство, может брать любую строку но редактор будет разрешать только определённые подсказанные(hint). Так как не определенно флагов использования, по умолчанию это PROPERTY_USAGE_STORAGE и PROPERTY_USAGE_EDITOR.

Существует много подсказок и флагов использования доступные в object.h, проверьте их.

Свойства могут также работать как свойства C# и могут быть доступные из скрипта через индексирование, но это их использование в целом не приветствуется, так использования функций предпочтительнее для понятности. Многие свойства также разбиты на категории, такие как "animation/frame" которые делают индексирование невозможным разве что с использованием оператора [].

Из _bind_methods(), свойства могут быть созданы и связаны с соответствующими существующими функциями set/get. Например:

ADD_PROPERTY(PropertyInfo(Variant::INT, "amount"), "set_amount", "get_amount")

Это создаёт свойство использующее сеттер и геттер.

Связывание свойств через _set/_get/_get_property_list

Дополнительный метод для создания свойств существует для достижения большей гибкости (т.е добавления или удаления свойств в контексте).

Следующие функции могут быть перезаписаны в наследнике Object, они НЕ виртуальные, НЕ ДЕЛАЙТЕ их виртуальными, они вызываются для каждой перезаписи и предыдущие не удаляются (мультиуровневый вызов).

protected:
     void _get_property_list(List<PropertyInfo> *r_props) const;      // return list of properties
     bool _get(const StringName &p_property, Variant &r_value) const; // return true if property was found
     bool _set(const StringName &p_property, const Variant &p_value); // return true if property was found

Это также менее эффективно поскольку p_property должно быть последовательно сравнено со списком имен.

Динамическое приведение

Godot позволяет динамическое приведение между классами наследниками Object, к примеру:

void somefunc(Object *some_obj) {

     Button *button = Object::cast_to<Button>(some_obj);
}

Если приведение неудачно, возвращается NULL. Эта система использует RTTI, но также может хорошо работать (может немного медленно) когда RTTI отключено. Это полезно для платформ где маленькие размеры бинарного файла предпочтительнее, такое как HTML5 или консоли(с медленным доступом к памяти).

Сигналы

Для объектов можно определить набор сигналов (аналогично делегатам в других языках). В этом примере показано, как к ним подключиться:

obj->connect(<signal>, target_instance, target_method)
// for example:
obj->connect("enter_tree", this, "_node_entered_tree")

Метод _node_entered_tree должен быть зарегистрирован в классе с помощью ClassDB::bind_method (объяснялось ранее).

Добавление сигналов в класс делается в _bind_methods, через использование макроса ADD_SIGNAL например:

ADD_SIGNAL(MethodInfo("been_killed"))

Уведомления

У всех объектов Godot есть метод _notification, который позволяет им реагировать на обратные вызовы уровня движка, которые могут к ним относиться. Подробнее см. на странице Уведомления Godot.

Ссылки

RefCounted наследует от Object и содержит счётчик ссылок. Он является базовым для типов объектов с подсчётом ссылок. Их объявление должно осуществляться с использованием шаблона Ref<>. Например:

class MyReference: public RefCounted {
    GDCLASS(MyReference, RefCounted);
};

Ref<MyReference> myref(memnew(MyReference));

myref считает ссылки. Он может быть освобождён когда нет больше шаблонов Ref<> указывающих на него.

Источники:

Ресурсы

Resource наследует от RefCounted, поэтому все ресурсы подсчитываются по ссылкам. Ресурсы могут содержать путь, ссылающийся на файл на диске. Его можно задать с помощью resource.set_path(path), хотя обычно это делает загрузчик ресурсов. Два разных ресурса не могут иметь одинаковый путь; попытка сделать это приведёт к ошибке.

Ресурсы без пути тоже возможны.

Источники:

Загрузка Ресурсов

Ресурсы могут быть загружены через ResourceLoader API, например так:

Ref<Resource> res = ResourceLoader::load("res://someresource.res")

Если ссылка на этот ресурс уже загружена до этого и находится в памяти, ресурсный загрузчик вернёт эту ссылку. Это означает что только один ресурс загруженный из файла ссылается на диск в данное время.

  • resourceinteractiveloader (TODO)

Источники:

Сохранение Ресурсов

Сохранение ресурса может быть сделано через API сохранения ресурсов:

ResourceSaver::save("res://someresource.res", instance)

Экземпляр будет сохранён, а вложенные ресурсы, имеющие путь к файлу, будут сохранены как ссылка на этот ресурс. Вложенные ресурсы без пути будут объединены с сохранённым ресурсом и им будут назначены вложенные идентификаторы, например, res://someresource.res::1. Это также помогает кэшировать их при загрузке.

Источники: