表达式估值

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

表达式可以是:

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

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

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

备注

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

基本用法

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

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

可以使用以下操作符:

操作符

注意

+

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

减(-

乘(*

除(/

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

求余(%

返回整数除法的余数。

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

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

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

  1. # Returns 1.
  2. [1, 2][0]
  3. # Returns 3. Negative indices can be used to count from the end of the array.
  4. [1, 3][-1]
  5. # Returns "green".
  6. {"favorite_color": "green"}["favorite_color"]
  7. # All 3 lines below return 7.0 (Vector3 is floating-point).
  8. Vector3(5, 6, 7)[2]
  9. Vector3(5, 6, 7)["z"]
  10. Vector3(5, 6, 7).z

向表达式传递变量

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

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

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

为表达式设置基础实例

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

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

  1. func double(number):
  2. return number * 2
  3. func _ready():
  4. var expression = Expression.new()
  5. expression.parse("double(10)")
  6. # This won't work since we're not passing the current script as the base instance.
  7. var result = expression.execute([], null)
  8. print(result) # null
  9. # This will work since we're passing the current script (i.e. self)
  10. # as the base instance.
  11. result = expression.execute([], self)
  12. print(result) # 20

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

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

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

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

警告

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

示例脚本

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

  1. const DAYS_IN_YEAR = 365
  2. var script_member_variable = 1000
  3. func _ready():
  4. # Constant mathexpression.
  5. evaluate("2 + 2")
  6. # Math expression with variables.
  7. evaluate("x + y", ["x", "y"], [60, 100])
  8. # Call built-in method (hardcoded in the Expression class).
  9. evaluate("deg2rad(90)")
  10. # Call user method (defined in the script).
  11. # We can do this because the expression execution is bound to `self`
  12. # in the `evaluate()` method.
  13. # Since this user method returns a value, we can use it in math expressions.
  14. evaluate("call_me() + DAYS_IN_YEAR + script_member_variable")
  15. evaluate("call_me(42)")
  16. evaluate("call_me('some string')")
  17. func evaluate(command, variable_names = [], variable_values = []) -> void:
  18. var expression = Expression.new()
  19. var error = expression.parse(command, variable_names)
  20. if error != OK:
  21. push_error(expression.get_error_text())
  22. return
  23. var result = expression.execute(variable_values, self)
  24. if not expression.has_execute_failed():
  25. print(str(result))
  26. func call_me(argument = null):
  27. print("\nYou called 'call_me()' in the expression text.")
  28. if argument:
  29. print("Argument passed: %s" % argument)
  30. # The method's return value is also the expression's return value.
  31. return 0

脚本的输出将会是:

  1. 4
  2. 160
  3. 1.570796
  4. You called 'call_me()' in the expression text.
  5. 1365
  6. You called 'call_me()' in the expression text.
  7. Argument passed: 42
  8. 0
  9. You called 'call_me()' in the expression text.
  10. Argument passed: some string
  11. 0

内置函数

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

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