運算式求值

Godot 提供 Expression 類別,可用來計算運算式。

運算式可以是:

  • 數學運算式,例如 (2 + 4) * 16/4.0

  • 布林運算式,例如 true && false

  • 內建函式呼叫,例如 deg_to_rad(90)

  • 如果於呼叫 Expression.execute() 時將 base_instance 設為非 null,則可以呼叫用戶自訂腳本的方法,如 update_health()

備註

Expression 類別獨立於 GDScript,即使編譯 Godot 時未啟用 GDScript 模組也可使用。

基本用法

要計算數學運算式,請使用:

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]

減法(-

乘法(*

除法(/

若兩個運算元皆為整數則執行整數除法;只要其中一個為浮點數則返回浮點數結果。

餘數(%

回傳整數除法的餘數(取模)。結果符號與被除數相同。

邏輯 AND(&&

回傳布林值 AND 的結果。

邏輯 OR(||

回傳布林值 OR 的結果。

邏輯 NOT(!

回傳布林值 NOT 的結果。

運算子前後空格可加可不加。請注意一般的 運算子優先順序 依然適用。有需要時可用括號改變運算順序。

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 boolean expression.
    evaluate("true && false")
    # Boolean expression with variables.
    evaluate("!(a && b)", ["a", "b"], [true, false])

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

    # Call built-in method (built-in math function call).
    evaluate("deg_to_rad(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

此腳本的輸出為:

false
true
4
160
1.5707963267949

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

內建函式

Global Scope 中的所有方法皆可於 Expression 類別中直接使用,即使未綁定基礎實例亦可。方法參數與回傳型別相同。

但與 GDScript 不同,這些函式的所有參數**皆必填**,即使在類別文件中標記為可選也一樣。若有綁定基礎實例,則自訂函式則不受此限制。