class 属性的访问器有用吗?

Are accessors of class properties useful?

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


矢量2global_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)

那些访问器没用吗?

它们并非毫无用处。它们很有用……如果节点在场景树中。

您可以使用add_child or add_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)

因此,如您所见,它将具有与设置的不同的全局位置。而差异取决于 parent 的位置。设置 position 会产生同样的效果。 因此设置 global_position 在这里没有用。


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

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。如果你在源上搜索它,你会发现当节点退出场景树或修改变换时它被设置为真(即在 _notify_transform 中,我不包括在这里,但你可以看到它在场景树中时被 _update_transform 调用。

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

  1. 属性 global_position 只是方法 get_global_positionset_global_position.
  2. 的语法糖
  3. 方法get_global_positionset_global_position作用于全局变换,它继承自CanvasItem。
  4. 为了计算出全局位置,我们需要计算 parent 节点上的变换。 这意味着,我们也可以通过相同的过程来弄清楚,因此这些方法在技术上是不必要的。
  5. 这是懒惰完成的。全局位置失效,并根据需要重新计算。
  6. 如果节点不在场景树中……什么parent节点?在这种情况下,使用 global_position 与我们使用 position 的效果相同。 因此,当节点不在场景树中时,我们可以说 global_position 没有用。
  7. 有一个断言告诉你什么时候使用它,它不在场景树中。该断言为您提供了您发布的消息。