【问题标题】:How to create a getter function without a setter function?如何在没有 setter 函数的情况下创建 getter 函数?
【发布时间】:2022-07-23 01:39:40
【问题描述】:

我的脚本中有多个导出的变量,每当更改一个变量时,我想调用一个通用 getter 并让值自动设置

tool

export(float) var sample1 setget ,smthn_changed;
export(float) var sample2 setget ,smthn_changed;
export(float) var sample3 setget ,smthn_changed;

func smthn_changed():
    print("something changed!")

但这不起作用,我必须为每个变量创建一个设置器

有什么解决办法吗?

【问题讨论】:

    标签: godot gdscript


    【解决方案1】:

    请注意,您将smthn_changed 定义为这些属性的getter。当您尝试读取它们时调用 getter,而不是在您尝试分配它们时调用。

    好的,假设您确实想知道何时分配变量。为此,您通常会使用 setter,如下所示:

    export var property:bool setget set_property
    
    func set_property(new_value:bool) -> void:
        if property == new_value:
            return
    
        property = new_value
        print("value changed") # or emit a signal or whatever
    

    setter 将在变量被外部分配的任何时候被调用(或在内部使用self.property = value,如果您不使用self,您可以直接分配变量而无需触发 setter)。

    但是,由于您需要从 setter 写入实际变量,这意味着为每个变量创建一个 setter(如果您对多个变量使用相同的 setter,您将不知道要设置哪个)。


    您还可以尝试其他方法:_set_set 的问题是只会为未在脚本中声明的变量调用。

    所以,计划如下:

    • 我们要用不同的名称声明支持变量,而不是导出它们。
    • 我们将使用_set_set 来处理它们。
    • 我们将使用_get_property_list 来导出它们。

    让我们看看只有一个变量的情况:

    tool
    extends Spatial
    
    
    var _x:String setget _no_set
    
    
    func _set(property: String, value) -> bool:
        if property == "x":
            _x = value
            smth_changed()
            return true
    
        return false
    
    
    func _get(property: String):
        if property == "x":
            return _x
    
        return null
    
    
    func _get_property_list() -> Array:
        if not Engine.editor_hint or not is_inside_tree():
            return []
    
        return [
            {
                name = "x",
                type = TYPE_STRING,
                usage = PROPERTY_USAGE_DEFAULT
            }
        ]
    
    
    func _no_set(_new_value) -> void:
        pass
    
    
    func smth_changed() -> void:
        print("something changed!")
    

    与简单的 setter 相比,这不值得付出努力。

    setter _no_set 是一个什么都不做的setter(甚至不设置变量)。我已添加它以通过直接设置支持变量来防止从外部绕过机制。 您可以在此处添加警告,因为这不是您的代码应该做的事情。另一方面,您的代码不应该这样做也可以作为反对使用_no_set的论据。

    但让我们看看它是如何扩展到多个变量的:

    tool
    extends Spatial
    
    
    var _x:String setget _no_set
    var _y:String setget _no_set
    
    
    func _set(property: String, value) -> bool:
        match property:
            "x":
                _x = value
            "y":
                _y = value
            _:
                return false
    
        smth_changed()
        return true
    
    
    func _get(property: String):
        match property:
            "x":
                return _x
            "y":
                return _y
    
        return null
    
    
    func _get_property_list() -> Array:
        if not Engine.editor_hint or not is_inside_tree():
            return []
    
        return [
            {
                name = "x",
                type = TYPE_STRING,
                usage = PROPERTY_USAGE_DEFAULT
            },
            {
                name = "y",
                type = TYPE_STRING,
                usage = PROPERTY_USAGE_DEFAULT
            }
        ]
    
    
    func _no_set(_new_value) -> void:
        pass
    
    
    func smth_changed() -> void:
        print("something changed!")
    

    仍然不是很好,因为我们必须多次重复变量。我仍然希望有多个 setter,即使它们都有相同的代码。


    任意一组属性的一般情况很棘手,因为从_get 调用get,或从_setget_property_listget_property_list 调用_get_property_list 会导致堆栈溢出会使 Godot 崩溃(并在打开项目时继续崩溃)。 所以写这段代码的时候要小心。

    为了避免从_get_property_list 调用get_property_list,我要做的是将我们想要的属性放入字典中:

    tool
    extends Spatial
    
    
    var _properties := {
        "x": "",
        "y": ""
    } setget _no_set, _no_get
    
    
    func _set(property: String, value) -> bool:
        if _properties.has(property):
            _properties[property] = value
            smth_changed()
            return true
    
        return false
    
    
    func _get(property: String):
        if _properties.has(property):
            return _properties[property]
    
        return null
    
    
    func _get_property_list() -> Array:
        if not Engine.editor_hint or not is_inside_tree():
            return []
    
        var result := []
        for property_name in _properties.keys():
            result.append(
                {
                    name = property_name,
                    type = typeof(_properties[property_name]),
                    usage = PROPERTY_USAGE_DEFAULT
                }
            )
    
        return result
    
    
    func _no_set(_new_value) -> void:
        pass
    
    
    func _no_get():
        return null
    
    
    func smth_changed() -> void:
        print("something changed!")
    

    另请注意,我正在根据 typeof 的值报告类型。

    我将由您来决定这种方法是否值得努力。例如,如果变量集可以更改,则可能是这样。 我提醒你,你可以调用 property_list_changed_notify 以便 Godot 调用 _get_property_list 并使用新的属性集更新检查器面板。

    尽管有_no_set,字典仍然可以在外部读取和操作。所以我添加了一个 getter _no_get,它返回 null 来防止这种情况。 如果您喜欢 _no_set 中的警告,您可能也希望 _no_get 中的警告。


    附录:这是一个变体,它使用数组作为要导出的属性的名称。这样,您仍然可以使用常规变量而不是处理字典。让数组保持最新状态由您决定。

    tool
    extends Spatial
    
    
    var _property_names := ["x", "y"] setget _no_set, _no_get
    var _x:String
    var _y:String
    
    
    func _set(property: String, value) -> bool:
        if _property_names.has(property):
            set("_" + property, value)
            smth_changed()
            return true
    
        return false
    
    
    func _get(property: String):
        if _property_names.has(property):
            return get("_" + property)
    
        return null
    
    
    func _get_property_list() -> Array:
        if not Engine.editor_hint or not is_inside_tree():
            return []
    
        var result := []
        for property_name in _property_names:
            if not "_" + property_name in self:
                push_warning("Not existing variable: " + property_name)
                continue
    
            result.append(
                {
                    name = property_name,
                    type = typeof(get("_" + property_name)),
                    usage = PROPERTY_USAGE_DEFAULT
                }
            )
    
        return result
    
    
    func _no_set(_new_value) -> void:
        pass
    
    
    func _no_get():
        return null
    
    
    func smth_changed() -> void:
        print("something changed!")
    

    请注意,我添加了一项检查以防止在没有支持变量的情况下导出,这也会发出警告。暴露它们并不是灾难性的,因为它们只会被视为 null。

    另外请注意,我必须从这个版本的变量中删除_no_set。原因是我使用set 设置它们,这导致调用setter,并且由于_no_set 没有设置变量,结果是它没有保存值。


    关于重置值的附录

    如果你想添加那个箭头来重置你需要实现几个 (yikes) 未记录方法的值:

    func property_can_revert(property:String) -> bool:
        if property in self:
            return true
    
        return false
    
    
    func property_get_revert(property:String):
        match typeof(get(property)):
            TYPE_NIL:
                return null
            TYPE_BOOL:
                return false
            TYPE_INT:
                return 0
            TYPE_REAL:
                return 0.0
            TYPE_STRING:
                return ""
            TYPE_VECTOR2:
                return Vector2()
            TYPE_RECT2:
                return Rect2()
            TYPE_VECTOR3:
                return Vector3()
            TYPE_TRANSFORM2D:
                return Transform2D()
            TYPE_PLANE:
                return Plane()
            TYPE_QUAT:
                return Quat()
            TYPE_AABB:
                return AABB()
            TYPE_BASIS:
                return Basis()
            TYPE_TRANSFORM:
                return Transform()
            TYPE_COLOR:
                return Color()
            TYPE_NODE_PATH:
                return NodePath()
            TYPE_RID:
                return RID(Object())
            TYPE_OBJECT:
                return Object()
            TYPE_DICTIONARY:
                return {}
            TYPE_ARRAY:
                return []
            TYPE_RAW_ARRAY:
                return PoolByteArray()
            TYPE_INT_ARRAY:
                return PoolIntArray()
            TYPE_REAL_ARRAY:
                return PoolRealArray()
            TYPE_STRING_ARRAY:
                return PoolStringArray()
            TYPE_VECTOR2_ARRAY:
                return PoolVector2Array()
            TYPE_VECTOR3_ARRAY:
                return PoolVector3Array()
            TYPE_COLOR_ARRAY:
                return PoolColorArray()
    
        return null
    

    这个想法是property_can_revert 将为任何具有重置箭头的属性返回trueproperty_get_revert 将给出当您单击所述箭头时将设置的值。 这必须在 source code 中找到,因为它没有记录在案。

    【讨论】:

    • 就像一个魅力,但如果我希望类型为export(float, -100,100,5)
    • 导出的变量似乎没有撤消箭头
    • @cakelover 关于export(float, -100,100,5) 之类的内容,除了我在这里展示的nametypeusage,您还需要hinthint_string。请参阅 _get_property_listproperty hint。我不知道如何添加重置箭头。
    • @cakelover 我找到了如何做箭头,将其添加到答案中。
    • @cakelover _properties 是一个无需设置即可修改的对象。最终取决于你的纪律,不要修改它。同样,首先不允许修改取决于您的纪律。毕竟,您可以随时更改代码。我的偏见是认知负荷较小的东西,从长远来看意味着更少的努力,即使这意味着在短期内付出更多的努力。我很懒。因此,我更喜欢自动化,而且,我更喜欢系统阻止我做我不想做的事情,而不是受制于我的意志力。但您可能更喜欢不同的方法。
    猜你喜欢
    • 2017-06-11
    • 2017-11-28
    • 1970-01-01
    • 1970-01-01
    • 2017-10-12
    • 2018-12-11
    • 2021-04-19
    • 2022-06-10
    • 1970-01-01
    相关资源
    最近更新 更多