请注意您正在定义smthn_changed
作为这些属性的吸气剂。当您尝试读取吸气剂时,而不是当您尝试分配它们时,将调用吸气剂。
好吧,假设您确实想知道变量何时被赋值。为此,您通常会使用设置器,如下所示:
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,您将不知道要设置哪个)。
您还可以尝试其他方法:_set
。问题与_set
是只会为脚本中未声明的变量调用。
所以,这是计划:
- 我们将使用不同的名称声明支持变量,而不是导出它们。
- 我们将使用
_set
and _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 相比,这不值得付出努力。
二传手_no_set
是一个不执行任何操作的设置器(甚至不设置变量)。我添加它是为了防止通过直接设置支持变量来从外部绕过该机制。您可以在那里添加警告,因为这不是您的代码应该做的事情。另一方面,您的代码不应该这样做的事实也可以被视为反对的论据_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!")
仍然不太好,因为我们必须多次重复变量。我仍然希望有多个设置器,即使它们都有相同的代码。
任意一组属性的通用情况很棘手,因为调用get
from _get
, or set
from _set
, or get_property_list
form _get_property_list
以这种方式导致堆栈溢出将使 Godot 崩溃(并在打开项目时继续崩溃)。所以写这段代码的时候要小心。
我要做什么才能避免打电话get_property_list
from _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
以至于戈多呼唤_get_property_list
并使用新的属性集更新检查器面板。
尽管_no_set
,字典仍然可以在外部读取和操作。所以我添加了一个吸气剂_no_get
返回 null 来防止这种情况发生。如果您喜欢在您的_no_set
,您可能希望在您的_no_get
too.
Addendum:这是一个变体,它使用数组作为要导出的属性的名称。这样你仍然可以拥有常规变量而不用处理字典。您有责任保持阵列最新。
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!")
请注意,我添加了一项检查以防止在没有支持变量的情况下导出,这也会发出警告。暴露它们并不是灾难性的,因为它们只会被视为空。
另请注意,我必须删除_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
将返回true
对于任何将具有重置箭头的属性。和property_get_revert
将给出单击所述箭头时将设置的值。这必须在源代码因为它没有记录。