Класс Object

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

:ref:`Object <class_object>`это базовый класс почти для всего. Большинство классов в Godot наследуется от него напрямую или косвенно. Объекты предоставляют рефлексию и редактируемые свойства, а также их объявление важно для использования простых макросов как этот.

class CustomObject : public Object {

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

Это делает Объекты многофункциональными, например

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::register_method(D_METHOD("methodname", "arg1name", "arg2name"), &MyCustomMethod);

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

ClassDB::register_method(D_METHOD("methodname", "arg1name", "arg2name"), &MyCustomType::method, DEFVAL(-1)); // default value for arg2name

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

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

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

Источники:

Константы

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

enum SomeMode {
   MODE_FIRST,
   MODE_SECOND
};

For these to work when binding to methods, the enum must be declared convertible to int, for this a macro is provided:

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

The constants can also be bound inside _bind_methods, by using:

BIND_CONSTANT(MODE_FIRST);
BIND_CONSTANT(MODE_SECOND);

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

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

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

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

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

For example:

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

Это целочисленное свойство, названное «amount», hint это диапазон, от 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, они НЕ виртуальные, НЕ ДЕЛАЙТЕ их виртуальными, они вызываются для каждой перезаписи и предыдущие не удаляются (мультиуровневый вызов).

void _get_property_info(List<PropertyInfo> *r_props); // 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::register_method (объяснён ранее).

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

ADD_SIGNAL(MethodInfo("been_killed"))

References

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

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

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

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

Источники:

Ресурсы:

Resource наследуется от Reference, чтобы все ресурсы могли считать ссылки. Ресурсы могут опционально содержать путь, который ссылается в файл на диске. Он может быть установлен через 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». Это также помогает кэшировать их при загрузке.

Источники: