Object 类

参见

本页介绍了Godot中对象的C++实现. 寻找Object类参考? 请看这里.

一般定义

Object 几乎是所有类的基类.Godot中的大多数类都直接或间接继承自它.Object对象提供反射和可编辑的属性, 声明它们就像使用单个宏一样.

class CustomObject : public Object {

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

这使得 Object 获得了很多功能,例如

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.

参考:

注册对象

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"), &MyCustomMethod);

参数的默认值可以按相反的顺序传递:

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

D_METHOD 是一个宏, 它将 methodname 转换为StringName以提高效率. 参数名称用于自我检查, 但在发布时进行编译时, 宏会忽略它们, 因此未使用字符串从而对其进行了优化.

有关更多示例, 请查看Control或Object的 _bind_methods .

如果只是添加不希望被彻底记录的模块和功能, 可以安全地忽略 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);

属性(设置/获取)

对象导出属性, 这些属性可用于以下用途:

  • 序列化和反序列化对象.

  • 为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")

这是一个字符串属性, 可以接受任何字符串, 但编辑器只允许定义的提示字符串. 由于未指定使用标志, 因此默认值为 PROPERTY_USAGE_STORAGEPROPERTY_USAGE_EDITOR.

在object.h中有很多提示和用法标记, 请对其进行检查.

属性也可以像C#属性一样工作, 并且可以使用索引从脚本访问, 但通常不鼓励这种用法, 因为使用函数是易读性的首选. 许多属性也与类别绑定, 例如 动画/帧, 除非使用操作符 [], 否则也无法建立索引.

_bind_methods() 开始, 只要存在set/get函数, 就可以创建和绑定属性. 例如:

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

这将使用setter和getter创建属性.

使用 _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 (前面解释过)注册到类.

使用 ADD_SIGNAL 宏在 _bind_methods 中添加信号到类中, 例如:

ADD_SIGNAL(MethodInfo("been_killed"))

通知

All objects in Godot have a _notification method that allows it to respond to engine level callbacks that may relate to it. More information can be found on the Godot 通知 page.

参考

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)

实例将被保存。有文件路径的子资源将被保存为对该资源的引用。没有路径的子资源将与保存的资源捆绑在一起,并分配子 ID,如 res://someresource.res::1。这也有助于在加载时对其进行缓存。

参考: