请注意,您将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,或从_set 或get_property_list 从get_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 将为任何具有重置箭头的属性返回true。 property_get_revert 将给出当您单击所述箭头时将设置的值。 这必须在 source code 中找到,因为它没有记录在案。