Godot4.x的整体架构图解析-源码阅读
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 pointeruint8_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.hif (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 hereif (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 = falsepending_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官网的架构图是比较老的版本的,因此有的模块已经不存在或者名字发生了改变。