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 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就是一个可以代表/指代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。
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);
        }
        
  • 总结:因此我们可以看到Sprite2D模块跟RenderinngServer之间基本上是依赖关系,Sprite2D依赖RenderingServer进行纹理的绘制。

总结

  • 上述的架构图上没有包含所有的Class,只列举了主要的部分。
  • Godot官网的架构图是比较老的版本的,因此有的模块已经不存在或者名字发生了改变。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值