对象类

一般定义

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.

参考文献:

注册对象

ClassDB是一个静态类,它包含从对象继承的注册类的整个列表,以及到所有方法属性和整型常量的动态绑定。

类通过调用注册:

ClassDB::register_class<MyCustomClass>()

注册它将允许脚本、代码实例化类,或者在反序列化时重新创建它们。

注册为虚拟是相同的,但不能实例化。

ClassDB::register_virtual_class<MyCustomClass>()

对象派生类可以重写静态函数 static void _bind_methods() . 当注册一个类时,调用这个静态函数来注册所有对象方法、属性、常量等。它只调用一次。如果对象派生类已实例化但尚未注册,则它将自动注册为虚拟类。

里面 _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 控制或对象的。

如果只是添加不希望被完整记录的模块和功能,那么 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);

属性(设置/获取)

对象导出属性,属性对以下内容很有用:

  • 对对象进行序列化和反序列化。

  • 为对象派生类创建可编辑值列表。

属性通常由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")

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

在object.h中有很多提示和使用标志,请检查一下。

属性也可以像C# properties一样工作,并且可以使用索引从脚本中访问,但是通常不鼓励使用这种用法,因为使用函数更适合易读性。许多属性也与类别绑定,例如“动画/帧”,除非使用运算符[],否则这些属性也会使索引变得不可能。

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

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

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

绑定属性使用 _set/_get /_get_property_list

如果需要更大的灵活性(即在上下文中添加或删除属性),则存在创建属性的其他方法。

以下函数可以在对象派生类中被重写,它们不是虚拟的,不使它们成为虚拟的,为每个重写调用它们,并且前面的函数不会无效(多级调用)。

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提供对象派生类之间的动态转换,例如:

void somefunc(Object *some_obj) {

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

如果强制转换失败,则返回空值。这个系统使用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"))

工具书类

Reference 从对象继承并保留引用计数。它是引用计数对象类型的基础。声明它们必须使用ref<>模板完成。例如:

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

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

myref 是否计算引用。当没有更多的引用模板指向它时,它将被释放。

参考文献:

资源:

Resource 从引用继承,因此所有资源都被引用计数。资源可以选择包含引用磁盘上文件的路径。这个可以用 resource.set_path(path) . 不过,这通常是由资源加载器完成的。任何两个不同的资源都不能有相同的路径,尝试这样做将导致错误。

没有路径的资源也很好。

参考文献:

资源加载

可以使用ResourceLoader API加载资源,如下所示:

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

如果对该资源的引用以前已加载并在内存中,则资源加载器将返回该引用。这意味着同时只能从磁盘上引用的文件加载一个资源。

  • 资源交互加载程序(TODO)

参考文献:

资源节约

可以使用资源保护程序API来保存资源:

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

将保存实例。具有文件路径的子资源将另存为对该资源的引用。没有路径的子资源将与保存的资源和分配的子ID捆绑在一起,如“res://someresource.res::1”。这也有助于在加载时缓存它们。

参考文献: