【问题标题】:Are accessors of class properties useful?类属性的访问器有用吗?
【发布时间】:2021-04-29 21:42:13
【问题描述】:

考虑类Node2D的属性global_position的以下访问器方法:


Vector2 全局位置

  • 二传手 set_global_position(value)

  • Getter get_global_position()


但是属性没有被封装,如本例所示:

tool

extends EditorScript

func _run() -> void:
    var n = Node2D.new()
    n.global_position = Vector2(100, 100)
    print(n.global_position)

产生:

* scene/2d/canvas_item.cpp:467 - Condition "!is_inside_tree()" is true. Returned: get_transform()
(100, 100)

那些访问器没用吗?

【问题讨论】:

    标签: encapsulation accessor gdscript


    【解决方案1】:

    它们不是无用的。它们很有用……如果节点在场景树中。

    您可以使用add_childadd_child_below_node 将节点添加到场景树中。


    我不确定您所说的“未封装”是什么意思。以防万一,我会指出你并没有绕过它们。

    当您使用该属性时,您使用的是 getter 和 setter 方法。该属性为语言绑定提供了便利。因此,我们也可以说您不需要属性,只需要 getter 和 setter。

    您可以在source for Node2D 中看到,有一个_bind_methods 函数可以设置所有公开使用的属性和方法。这就是global_position 的样子:

    ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
    

    您收到的消息是因为您在不在场景树上的节点上使用global_position。而且,是的,在这种情况下,它没有用。我们可以通过一个简单的脚本来解决这个问题:

    extends Node2D
    
    func _ready() -> void:
        global_position = Vector2(200, 300)
        var n = Node2D.new()
        n.global_position = Vector2(100, 100)
        print(n.global_position)
        add_child(n)
        print(n.global_position)
    

    这个输出:

    (100, 100)
    (300, 400)
    

    因此,如您所见,它将具有与设置不同的全局位置。并且差异取决于父母的位置。设置position 将具有相同的效果。 因此设置global_position 在这里没有用。


    如果您想更深入地了解 global_position 的作用,我们可以查看 getter 和 setter 的源代码(Node2D 的链接源代码的一部分):

    Point2 Node2D::get_global_position() const {
    
        return get_global_transform().get_origin();
    }
    
    void Node2D::set_global_position(const Point2 &p_pos) {
    
        Transform2D inv;
        CanvasItem *pi = get_parent_item();
        if (pi) {
            inv = pi->get_global_transform().affine_inverse();
            set_position(inv.xform(p_pos));
        } else {
            set_position(p_pos);
        }
    }
    

    这是set_position,顺便说一下(注意它写成pos):

    void Node2D::set_position(const Point2 &p_pos) {
    
        if (_xform_dirty)
            ((Node2D *)this)->_update_xform_values();
        pos = p_pos;
        _update_transform();
        _change_notify("position");
    }
    

    还有_update_transform(带有显眼的!is_inside_tree()勾选):

    void Node2D::_update_transform() {
    
        _mat.set_rotation_and_scale(angle, _scale);
        _mat.elements[2] = pos;
    
        VisualServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), _mat);
    
        if (!is_inside_tree())
            return;
    
        _notify_transform();
    }
    

    请注意,_update_transform 会根据 pos 更新 _mat

    get_global_transform 呢?不在那个文件里。我们在source for CanvasItem 中找到它:

    Transform2D CanvasItem::get_global_transform() const {
    #ifdef DEBUG_ENABLED
        ERR_FAIL_COND_V(!is_inside_tree(), get_transform());
    #endif
        if (global_invalid) {
            const CanvasItem *pi = get_parent_item();
            if (pi) {
                global_transform = pi->get_global_transform() * get_transform();
            } else {
                global_transform = get_transform();
            }
    
            global_invalid = false;
        }
    
        return global_transform;
    }
    

    还有你看到的失败断言:!is_inside_tree()

    哦,关于那个global_invalid。如果在源码上搜索,会发现在节点退出场景树或者transform被修改时设置为true(即在_notify_transform,这里我不包括,但是你可以看到在场景树中时由_update_transform调用)。

    我们能从这一切中得到什么?

    1. 属性global_position 只是方法get_global_positionset_global_position 的语法糖。
    2. get_global_positionset_global_position 方法用于全局变换,它继承自 CanvasItem。
    3. 要确定全局位置,我们需要计算父节点上的变换。 也就是说,我们也可以通过相同的过程来解决这个问题,因此这些方法在技术上是不必要的。
    4. 这是懒惰的。全局位置失效,并按需重新计算。
    5. 如果节点不在场景树中……什么父节点?在这种情况下,如果我们使用position,使用global_position 会做同样的事情。 因此,当节点不在场景树中时,我们可以说global_position 没有用处。
    6. 有一个断言会告诉您何时使用它并且它不在场景树中。该断言为您提供了您发布的信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多