Object 类
参见
本页介绍了Godot中对象的C++实现. 寻找Object类参考? 请看这里.
一般定义
Object is the base class for almost everything. Most classes in Godot inherit directly or indirectly from it. Declaring them is a matter of using a single macro like this:
class CustomObject : public Object {
GDCLASS(CustomObject, Object); // This is required to inherit from Object.
};
Objects come with a lot of built-in functionality, like reflection and editable properties:
CustomObject *obj = memnew(CustomObject);
print_line("Object class: ", obj->get_class()); // print object class
OtherClass *obj2 = Object::cast_to<OtherClass>(obj); // Converting between classes, similar to dynamic_cast
参考:
Registering Object classes
Most Object subclasses are registered by calling GDREGISTER_CLASS.
GDREGISTER_CLASS(MyCustomClass)
This will register it as a named, public class in the ClassDB, which will allow the class to be instantiated by
scripts, code, or by deserialization. Note that classes registered as GDREGISTER_CLASS should expect to be
instantiated or freed automatically, for example by the editor or the documentation system.
Besides GDREGISTER_CLASS, there are a few other modes of privateness:
// Registers the class publicly, but prevents automatic instantiation through ClassDB.
GDREGISTER_VIRTUAL_CLASS(MyCustomClass);
// Registers the class publicly, but prevents all instantiation through ClassDB.
GDREGISTER_ABSTRACT_CLASS(MyCustomClass);
// Registers the class in ClassDB, but marks it as private,
// such that it is not visible to scripts or extensions.
// This is the same as not registering the class explicitly at all
// - in this case, the class is registered as internal automatically
// when it is first constructed.
GDREGISTER_INTERNAL_CLASS(MyCustomClass);
// Registers the class such that it is only available at runtime (but not in the editor).
GDREGISTER_RUNTIME_CLASS(MyCustomClass);
It is also possible to use GDSOFTCLASS(MyCustomClass, SuperClass) instead of GDCLASS(MyCustomClass, SuperClass).
Classes defined this way are not registered in the ClassDB at all. This is sometimes used for platform-specific
subclasses.
Registering bindings
Object-derived classes can override the static function
static void _bind_methods(). When the class is registered, this
static function is called to register all the object methods,
properties, constants, etc. It's only called once.
在 _bind_methods 里面, 有几件事可以做. 注册函数是一个:
ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method);
Default values for arguments can be passed as parameters at the end:
ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method, DEFVAL(-1), DEFVAL(-2)); // Default values for arg2name (-1) and arg3name (-2).
Default values must be provided in the same order as they are declared, skipping required arguments and then providing default values for the optional ones. This matches the syntax for declaring methods in C++.
D_METHOD 是一个宏, 它将 methodname 转换为StringName以提高效率. 参数名称用于自我检查, 但在发布时进行编译时, 宏会忽略它们, 因此未使用字符串从而对其进行了优化.
有关更多示例, 请查看Control或Object的 _bind_methods .
如果只是添加不希望被彻底记录的模块和功能, 可以安全地忽略 D_METHOD() 宏, 并且为了简洁起见, 可以传递传递名称的字符串.
参考:
常量
类通常有枚举, 例如:
enum SomeMode {
MODE_FIRST,
MODE_SECOND
};
For these to work when binding to methods, the enum must be declared convertible to int. A macro is provided to help with this:
VARIANT_ENUM_CAST(MyClass::SomeMode); // now functions that take SomeMode can be bound.
常量也可以绑定在 _bind_methods 中, 通过使用:
BIND_CONSTANT(MODE_FIRST);
BIND_CONSTANT(MODE_SECOND);
属性(设置/获取)
对象导出属性, 这些属性可用于以下用途:
序列化和反序列化对象.
为Object派生类创建可编辑值列表.
Properties are usually defined by the PropertyInfo() class and constructed as:
PropertyInfo(type, name, hint, hint_string, usage_flags)
例如:
PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "0,49,1", PROPERTY_USAGE_EDITOR)
This is an integer property named "amount". The hint is a range, and the range goes from 0 to 49 in steps of 1 (integers). It is only usable for the editor (editing the value visually) but won't be serialized.
另一个示例:
PropertyInfo(Variant::STRING, "modes", PROPERTY_HINT_ENUM, "Enabled,Disabled,Turbo")
这是一个字符串属性, 可以接受任何字符串, 但编辑器只允许定义的提示字符串. 由于未指定使用标志, 因此默认值为 PROPERTY_USAGE_STORAGE 和 PROPERTY_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 必须按顺序与所需的名称进行比较, 因此效率也较低.
信号
Objects can have a set of signals defined (similar to Delegates in other languages). This example shows how to connect to them:
// This is the function signature:
//
// Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0)
//
// For example:
obj->connect("signal_name_here", callable_mp(this, &MyCustomType::method), CONNECT_DEFERRED);
callable_mp is a macro to create a custom callable function pointer to member functions.
For the values of p_flags, see ConnectFlags.
使用 ADD_SIGNAL 宏在 _bind_methods 中添加信号到类中, 例如:
ADD_SIGNAL(MethodInfo("been_killed"))
Object ownership and casting
Objects are allocated on the heap. There are two different ownership models:
Objects derived from
RefCountedare reference counted.All other objects are manually memory managed.
The ownership models are fundamentally different. Refer to the section for each respectively to learn how to create, store, and free the object.
When you do not know whether an object passed to you (via Object *) is RefCounted, and you need to store it,
you should store its ObjectID rather than a pointer (as explained below, in the manual memory management section).
When an object is passed to you via Variant, especially when using deferred callbacks, it is
possible that the contained Object * was already freed by the time your function runs.
Instead of converting directly to Object *, you should use get_validated_object:
void do_something(Variant p_variant) {
Object *object = p_variant.get_validated_object();
ERR_FAIL_NULL(object);
}
Manual memory management
Manually memory managed objects are created using memnew and freed using memdelete:
Node *node = memnew(Node);
// ...
memdelete(node);
node = nullptr;
When you are not the sole owner of an object, storing a pointer to it is dangerous: The object may at any point be freed through other references to it, causing your pointer to become a dangling pointer, which will eventually result in a crash.
When storing objects you are not the only owner of, you should store its ObjectID rather than a pointer:
Node *node = memnew(Node);
ObjectID node_id = node.get_instance_id();
// ...
Object *maybe_node = ObjectDB::get_instance(node_id);
ERR_FAIL_NULL(maybe_node); // The node may have been freed between calls.
RefCounted memory management
RefCounted subclasses are memory managed with reference counting semantics.
They are constructed using memnew, and should be stored in Ref instances. When the last Ref instance is
dropped, the object automatically self-destructs.
class MyRefCounted: public RefCounted {
GDCLASS(MyReference, RefCounted);
};
Ref<MyRefCounted> my_ref = memnew(MyRefCounted);
// ...
// Ref holds shared ownership over the object, so the object
// will not be freed. As long as you have a valid, non-null
// Ref, it can be safely assumed the object is still valid.
my_ref->get_class_name();
You should never call memdelete for RefCounted subclasses, because there may be other owners of it.
You should also never store RefCounted subclasses using raw pointers, for example
RefCounted *object = memnew(RefCounted). This is unsafe because other owners may destruct the object, leaving you
with a dangling pointer, which will eventually result in a crash.
参考:
动态转型
Godot在Object派生类之间提供动态转换, 例如:
void some_func(Object *p_object) {
Button *button = Object::cast_to<Button>(p_object);
}
If the cast fails, nullptr is returned. This works the same as dynamic_cast, but does not use
C++ RTTI.
通知
All objects in Godot have a _notification method that allows them to respond to engine-level callbacks that may relate to it. More information can be found on the Godot 通知 page.
资源
Resource inherits from RefCounted, so all resources
are reference counted. Resources can optionally contain a path, which
reference a file on disk. This can be set with resource.set_path(path),
though this is normally done by the resource loader. No two different
resources can have the same path; attempting to do so will result in an error.
资源也可以没有路径.
参考:
资源加载
可以使用ResourceLoader API加载资源, 如下所示:
Ref<Resource> res = ResourceLoader::load("res://someresource.res")
如果先前已加载对该资源的引用并且该引用在内存中, 则资源加载器将返回该引用. 这意味着只能同时从磁盘上引用的文件加载一个资源.
resourceinteractiveloader(TODO)
参考:
资源保存
可以使用资源保存器API保存资源:
ResourceSaver::save("res://someresource.res", instance)
The instance will be saved, and sub resources that have a path to a file will
be saved as a reference to that resource. Sub resources without a path will
be bundled with the saved resource and assigned sub-IDs, like
res://someresource.res::1. This also helps to cache them when loaded.