封装

已知,我们可以使用 <节点>.<属性变量> 的方式引用其他节点的属性变量并修改,但这样其实很危险,例如我们可能会不小心把玩家速度设置成负数,或将玩家的生命值设置过大导致超出生命上限。

在传统编程语言中,我们通常会给变量添加对应的访问方法,下例中的 设置移动速度 方法就是一个典型:

var 移动速度: int = 100

# 假设玩家最低移动速度是 10
func 设置移动速度(新速度:int):
    if 新速度 < 10:
        新速度 = 10
    移动速度 = 新速度

func 获取移动速度() -> int:
    return 移动速度

随后,我们只要保证每次用到 移动速度 时都通过调用 设置移动速度获取移动速度 方法即可。

[!note]

封装除了可以对属性值进行限制,还可以作为属性的唯一访问途径来监听属性值的变化,例如可以在 设置玩家速度 方法中加一个 print("玩家变速了!" + str(新速度)) 来提醒咱们玩家速度发生变化。

然后问题来了,肯定有一天我们会忘记 移动速度 属性还有两个对应的访问方法,这时候就需要用到 GDScript 为我们提供的 setget 关键字来指定变量的访问方法了:

var 移动速度: int = 100: set = 设置移动速度, get = 获取移动速度

在变量初始值后面加上了一个冒号,然后写上 set = XXX, get = XXX 这样的东西,这就为移动速度属性指定了两个访问方法。

在我们需要使用 移动速度 变量时,依旧按照普通变量的方式使用即可。就是说当执行 $"玩家".移动速度 = 400 这句代码时,就会自动调用 设置移动速度 方法并将 400 作为参数传入其中,相应的,执行 print($"玩家".移动速度) 时,实际输出的就是 获取移动速度 方法的返回值。

其实上例的代码可以简化,因为 获取移动速度 这个访问方法中没有进行任何操作,只是原样返回数值而已,所以可以省略掉这个方法和对应的 get = XXX

可运行的例子:

var 属性:int = 10:set = _设置属性, get = _获取属性

func _设置属性(新的值:int):
    if 新的值 > 0:
        属性 = 新的值
    else:
        属性 = 新的值 * 新的值

func _获取属性() -> int:
    if 属性 == 0:
        print("巧了,属性值竟然是零")
    return 属性

func _run():
    print(属性)
    属性 = 0
    print(属性)
    属性 = -22
    print(属性)

如果现在执行 _run 方法,则会看到如下输出:

10
巧了,属性值竟然是零
0
484

[!tip]

_设置属性_获取属性 方法作为属性的访问方法,咱一般不希望别人随便使用,所以起名字的时候给加上下划线前缀来告诉别人没事别用我。

简写形式

每次定义这种 设置xxx获取xxx 的方法也很麻烦,所以 Godot 给咱提供了一种简便的方式:

var 属性 = 100: 
    set(新的值):
        if 新的值 > 0:
            属性 = 新的值
        else:
            属性 = 新的值 * 新的值
    get:
        if 属性 == 0:
            print("巧了,属性值竟然是零")
        return 属性

这段代码与上面可运行的例子的逻辑相同,关键语法就是把 set = XXX 这种东西改成了一个类似名为 set 方法的结构,get 同理,但要注意这里不需要 func 关键字。