表达式估值

Godot 提供了 Expression 类,可以用来对表达式进行估值。

表达式可以是:

  • 类似 (2 + 4) * 16/4.0 的数学表达式。

  • 类似 deg2rad(90) 的内置方法调用。

  • 调用 Expression.execute() 时如果 base_instancenull,那么调用用户提供脚本的方法,比如 update_health()

备注

Expression 类是独立于 GDScript 的。即便禁用 GDScript 模块编译 Godot 也能使用。

基本用法

要对数学表达式求值,请使用:

var expression = Expression.new()
expression.parse("20 + 10*2 - 5/2.0")
var result = expression.execute()
print(result)  # 37.5

可以使用以下操作符:

操作符

注意

+

还可以用于连接字符串和数组:- "hello" + " world" = hello world - [1, 2] + [3, 4] = [1, 2, 3, 4]

减(-

乘(*

除(/

两个操作数都是整数时执行整数除法。如果至少有一个是浮点数,就会返回浮点值。

求余(%

返回整数除法的余数。

操作符周围的空格是可选的。另外请记住,此处适用一般的运算次序。必要时请使用括号来覆盖运算符的次序。

Godot 所支持的所有 Variant 类型都可以使用:整数、浮点数、字符串、数组、字典、颜色、向量……

数组与字典的索引方法与 GDScript 一致:

# Returns 1.
[1, 2][0]

# Returns 3. Negative indices can be used to count from the end of the array.
[1, 3][-1]

# Returns "green".
{"favorite_color": "green"}["favorite_color"]

# All 3 lines below return 7.0 (Vector3 is floating-point).
Vector3(5, 6, 7)[2]
Vector3(5, 6, 7)["z"]
Vector3(5, 6, 7).z

向表达式传递变量

你可以将变量传入表达式。这些变量就会进入这个表达式的“上下文”,在表达式中使用就会被替换:

var expression = Expression.new()
# Define the variable names first in the second parameter of `parse()`.
# In this example, we use `x` for the variable name.
expression.parse("20 + 2 * x", ["x"])
# Then define the variable values in the first parameter of `execute()`.
# Here, `x` is assigned the integer value 5.
var result = expression.execute([5])
print(result)  # 30

变量的名称和变量的值都必须以数组的形式指定,即便只定义一个变量也是如此。而且,变量名是大小写敏感的。

为表达式设置基础实例

表达式默认的基础实例是 null。这意味着该表达式没有关联基础实例。

调用 Expression.execute() 时,你可以将参数 base_instance 的值设为 self、另一个脚本实例、单例等特定对象的实例:

func double(number):
    return number * 2


func _ready():
    var expression = Expression.new()
    expression.parse("double(10)")

    # This won't work since we're not passing the current script as the base instance.
    var result = expression.execute([], null)
    print(result)  # null

    # This will work since we're passing the current script (i.e. self)
    # as the base instance.
    result = expression.execute([], self)
    print(result)  # 20

关联基础实例可以实现以下功能:

  • 在表达式中引用实例的常量(const)。

  • 在表达式中引用实例的成员变量(var)。

  • 在表达式中调用定义在实例上的方法,并使用其返回值。

警告

将基础实例设为非 null 值,就可以引用常量、成员变量、调用定义在该实例的附加脚本中的方法。允许用户输入表达式可能会导致在你的游戏出现作弊,如果你允许任意客户的在其他玩家的设备上运行表达式的话,甚至还可能引入安全隐患。

示例脚本

下面的脚本演示的是 Expression 类的功能:

const DAYS_IN_YEAR = 365
var script_member_variable = 1000


func _ready():
    # Constant mathexpression.
    evaluate("2 + 2")
    # Math expression with variables.
    evaluate("x + y", ["x", "y"], [60, 100])

    # Call built-in method (hardcoded in the Expression class).
    evaluate("deg2rad(90)")

    # Call user method (defined in the script).
    # We can do this because the expression execution is bound to `self`
    # in the `evaluate()` method.
    # Since this user method returns a value, we can use it in math expressions.
    evaluate("call_me() + DAYS_IN_YEAR + script_member_variable")
    evaluate("call_me(42)")
    evaluate("call_me('some string')")


func evaluate(command, variable_names = [], variable_values = []) -> void:
    var expression = Expression.new()
    var error = expression.parse(command, variable_names)
    if error != OK:
        push_error(expression.get_error_text())
        return

    var result = expression.execute(variable_values, self)

    if not expression.has_execute_failed():
        print(str(result))


func call_me(argument = null):
    print("\nYou called 'call_me()' in the expression text.")
    if argument:
        print("Argument passed: %s" % argument)

    # The method's return value is also the expression's return value.
    return 0

脚本的输出将会是:

4
160
1.570796

You called 'call_me()' in the expression text.
1365

You called 'call_me()' in the expression text.
Argument passed: 42
0

You called 'call_me()' in the expression text.
Argument passed: some string
0

内置函数

@GDScript 作用域中的大多数方法都可以在 Expression 类中使用,无需为表达式绑定基础实例。参数和返回类型也是一样的。

然而,与 GDScript 不同,参数始终是必须的,即使类参考中说明此参数为可选。不过,为表达式绑定了基础实例时,用户定义函数的参数没有此限制。