Godot整体架构图解析
阅读背景
Godot的架构图,官网中自有记载,比如4.x版本的Godot的架构图,如下所示:

上述这个构架图初次看的时候是感觉有些混乱,因为按照Godot开发文档来看,有些箭头是代表调用,有些代表继承,有些又代表组合…但是图中却都用箭头代表了,让人很容易混淆。
为了更好的理解Godot开发,利于自己理解Godot的类,特别是那些常见的类,比如Node、Node2D等,这个文章将参考Godot的开发文档绘制出Godot架构的UML类图。
Godot架构的UML类图

特殊关系的说明
从Godot编辑器上,有时很难获取这些类之间的关系,十分容易搞乱之间的对象关系。
因此下述将基于Godot4.4.1-stable的github源码以及个人理解,阐述其中一些容易混淆的关系。
Variant与Object:关联
- 首先查看core\variant\variant.h头文件
-
该头文件主要定义了Variant类声明,关键代码如下所示
class Variant { // 其他 private: // Variant takes 24 bytes when real_t is float, and 40 bytes if double. // It only allocates extra memory for AABB/Transform2D (24, 48 if double), // Basis/Transform3D (48, 96 if double), Projection (64, 128 if double), // and PackedArray/Array/Dictionary (platform-dependent). Type type = NIL; union { bool _bool; int64_t _int; double _float; Transform2D *_transform2d; ::AABB *_aabb; Basis *_basis; Transform3D *_transform3d; Projection *_projection; PackedArrayRefBase *packed_array; void *_ptr; //generic pointer uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)]{ 0 }; } _data alignas(8); // 其他 struct ObjData { ObjectID id; Object *obj = nullptr; void ref(const ObjData &p_from); void ref_pointer(Object *p_object); void ref_pointer(RefCounted *p_object); void unref(); template <typename T> _ALWAYS_INLINE_ void ref(const Ref<T> &p_from) { if (p_from.is_valid()) { ref(ObjData{ p_from->get_instance_id(), p_from.ptr() }); } else { unref(); } } }; } -
回答疑问:Godot编辑器中对Variant的介绍是20字节的空间占用,那么实际上是多少呢?
- 回答:Variant的内存占用是sizeof(Variant)=(8+4*sizeof(real_t)),因此如果real_t是float,那么Variant内存占用是
24字节Type type: 4bytes补齐8字节的空间: 4bytes_data空间: 16bytes
- 回答:Variant的内存占用是sizeof(Variant)=(8+4*sizeof(real_t)),因此如果real_t是float,那么Variant内存占用是
-
这里可以看出:Variant就是一个可以代表/指代Godot用到的所有数据类型的一个通用对象,那么Object在哪儿呢?
-
- 再来看看core\variant\variant.cpp源文件
- 重点关注Variant(Object* )初始化实现:
Variant::Variant(const Object *p_object) : type(OBJECT) { _get_obj() = ObjData(); _get_obj().ref_pointer(const_cast<Object *>(p_object)); } Variant::ObjData &Variant::_get_obj() { // _data._mem就是包含了Object对象指针的结构体 return *reinterpret_cast<ObjData *>(&_data._mem[0]); } void Variant::ObjData::ref_pointer(Object *p_object) { // Mirrors Ref::ref_pointer in refcounted.h if (p_object == obj) { return; } ObjData cleanup_ref = *this; if (p_object) { *this = ObjData{ p_object->get_instance_id(), p_object }; if (p_object->is_ref_counted()) { RefCounted *reference = static_cast<RefCounted *>(p_object); if (!reference->init_ref()) { *this = ObjData(); } } } else { *this = ObjData(); } cleanup_ref.unref(); } - 因此,很容易看出
_data._mem就是包含了Object对象指针的结构体,因此Variant关联了Object。
- 重点关注Variant(Object* )初始化实现:
Scene中的模块与Server中的模块:依赖
- 我们以Sprite2D模块的绘图进行举例说明,首先追踪路径
- 1、传入纹理
void Sprite2D::set_texture(const Ref<Texture2D> &p_texture) { if (p_texture == texture) { return; } if (texture.is_valid()) { texture->disconnect_changed(callable_mp(this, &Sprite2D::_texture_changed)); } texture = p_texture; if (texture.is_valid()) { texture->connect_changed(callable_mp(this, &Sprite2D::_texture_changed)); } queue_redraw(); // 2、纹理改变,入队重绘命令 emit_signal(SceneStringName(texture_changed)); item_rect_changed(); } - 2、纹理改变,入队重绘命令
void CanvasItem::queue_redraw() { ERR_THREAD_GUARD; // Calling from thread is safe. if (!is_inside_tree()) { return; } if (pending_update) { return; } pending_update = true; callable_mp(this, &CanvasItem::_redraw_callback).call_deferred(); // 3、 }- 3、进入画布基类的重绘命令
void CanvasItem::_redraw_callback() { if (!is_inside_tree()) { pending_update = false; return; } RenderingServer::get_singleton()->canvas_item_clear(get_canvas_item()); //todo updating = true - only allow drawing here if (is_visible_in_tree()) { drawing = true; current_item_drawn = this; notification(NOTIFICATION_DRAW); emit_signal(SceneStringName(draw)); GDVIRTUAL_CALL(_draw); current_item_drawn = nullptr; drawing = false; } //todo updating = false pending_update = false; // don't change to false until finished drawing (avoid recursive update) }- 3.1:
调用了RenderingServer执行清楚老的纹理RenderingServer::get_singleton()->canvas_item_clear(get_canvas_item()); - 3.2:发出NOTIFICATION_DRAW,执行Sprite2D的_notification
void Sprite2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { if (texture.is_null()) { return; } RID ci = get_canvas_item(); Rect2 src_rect, dst_rect; bool filter_clip_enabled; _get_rects(src_rect, dst_rect, filter_clip_enabled); texture->draw_rect_region(ci, dst_rect, src_rect, Color(1, 1, 1), false, filter_clip_enabled); // 下一步 } break; } } - 3.3:
Texture2D的绘制,调用了RenderingServer执行纹理的绘制void Texture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const { if (GDVIRTUAL_CALL(_draw_rect_region, p_canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_clip_uv)) { return; } RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, get_rid(), p_src_rect, p_modulate, p_transpose, p_clip_uv); }
- 1、传入纹理
- 总结:因此我们可以看到Sprite2D模块跟RenderinngServer之间基本上是依赖关系,Sprite2D依赖RenderingServer进行纹理的绘制。
总结
- 上述的架构图上没有包含所有的Class,只列举了主要的部分。
- Godot官网的架构图是比较老的版本的,因此有的模块已经不存在或者名字发生了改变。
854

被折叠的 条评论
为什么被折叠?



