对象类¶
一般定义¶
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”。这也有助于在加载时缓存它们。