Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

資料偏好

GDScript 是一個動態型別的高級語言,用於進行內容創作。語法類似於 Python (如區塊都是基於縮排來判斷,以及很多關鍵字都相同)。GDScript 的目標是要做一個對 Godot Engine 最佳化的語言,並且能緊密地與引擎整合,進而讓我們更有彈性地製作內容與整合功能。

GDScript 是完全獨立於 Python 存在的,沒有繼承或擴充關係。

歷史記錄

備註

關於 GDScript 歷史的文件移到了 常見問題

GDScript 範例

有些人瞭解語法後可以學得更好,所以以下是簡單的 GDScript 例子。

# Everything after "#" is a comment.
# A file is a class!

# (optional) icon to show in the editor dialogs:
@icon("res://path/to/optional/icon.svg")

# (optional) class definition:
class_name MyClass

# Inheritance:
extends BaseClass


# Member variables.
var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key": "value", 2: 3}
var other_dict = {key = "value", other_key = 2}
var typed_var: int
var inferred_type := "String"

# Constants.
const ANSWER = 42
const THE_NAME = "Charly"

# Enums.
enum {UNIT_NEUTRAL, UNIT_ENEMY, UNIT_ALLY}
enum Named {THING_1, THING_2, ANOTHER_THING = -1}

# Built-in vector types.
var v2 = Vector2(1, 2)
var v3 = Vector3(1, 2, 3)


# Functions.
func some_function(param1, param2, param3):
    const local_const = 5

    if param1 < local_const:
        print(param1)
    elif param2 > 5:
        print(param2)
    else:
        print("Fail!")

    for i in range(20):
        print(i)

    while param2 != 0:
        param2 -= 1

    match param3:
        3:
            print("param3 is 3!")
        _:
            print("param3 is not 3!")

    var local_var = param1 + 3
    return local_var


# Functions override functions with the same name on the base/super class.
# If you still want to call them, use "super":
func something(p1, p2):
    super(p1, p2)


# It's also possible to call another function in the super class:
func other_something(p1, p2):
    super.something(p1, p2)


# Inner class
class Something:
    var a = 10


# Constructor
func _init():
    print("Constructed!")
    var lv = Something.new()
    print(lv.a)

如果你曾經有使用如 C、C++、或 C# 等靜態型別語言的經驗卻沒用過動態型別的話,建議你閱讀這個教學: GDScript:動態語言簡介

語言

接下來是 GDScript 的概要說明。關於陣列或其他物件有哪些方法可以用之類的詳細說明,請參考相關連結裡的類別介紹。

識別項

所有只包含英文字元( azAZ )、數字( 09 )、與 _ 的字串都算是一個識別項。另外,識別項不可以以數字開頭。識別項的大小寫有別( fooFOO 是不同的)。

識別碼也可能包含 UAX#31 的大多數 Unicode 字元部分。這允許您使用以英語以外的語言編寫的識別符名稱。標識符中不允許使用被視為與 ASCII 字元和表情符號「混淆」的 Unicode 字元。

關鍵字

下面是 GDScript 所支援的關鍵字列表。這些單字是保留字(符記,Token),所以不能當作識別項來用。有些運算子(如 innotand 、或是 or )與下一個章節會提到的內建型別的名稱也是保留字。

如果你想瞭解一下的話,關鍵字定義在 GDScript Tokenizer 裡。

關鍵字

說明

if

請參考 if/else/elif

elif

請參考 if/else/elif

else

請參考 if/else/elif

for

請參考 for

while

請參考 while

match

請參考 match

break

跳出目前的 for 或是 while 迴圈。

continue

馬上跳至 forwhile 迴圈的下一個迭代。

階段

當語法上需要有敘述句但不需要執行任何東西的時候可以使用。如:空函式。

return

從函式裡回傳數值。

類別

Defines an inner class. See Inner classes.

class_name

Defines the script as a globally accessible class with the specified name. See Registering named classes.

extends

定義目前類別所要繼承的類別。

is

測試一個變數是否為繼承自給定的類別,或判斷其是否為指定的內建型別。

尋找

Tests whether a value is within a string, array, range, dictionary, or node. When used with for, it iterates through them instead of testing.

as

嘗試轉換為指定型別。

self

參照目前的類別實體。

signal

定義一個訊號。

func

定義一個函式。

static

定義一個靜態函式。不能用來定義靜態成員變數。

const

定義一個常數。

enum

定義一個 Enum(列舉型)。

var

定義一個變數。

breakpoint

除錯器斷點的編輯器助手。與透過點擊裝訂線建立的斷點不同,「斷點」會儲存在腳本本身中。這使得在使用版本控制時它可以在不同的機器上保持不變。

preload

預先載入一個類別或變數。請參閱 以類別作為資源

await

等待訊號或協程完成。見`等待訊號和協程`_。

yield

以前用於協程。保留為關鍵字,方便遷移。

assert

判定一個條件,當判定失敗的時候記錄錯誤。在非除錯用建置中會忽略。請參考 Assert 關鍵字

void

用於代表函式不返回任何值。

PI

PI(圓周率)常數。

TAU

TAU 常數。

INF

無窮大常數。用於比較。

NAN

NAN(Not a Number,不是數字)常數。用於比較。

運算子

下面是支援的運算子與其優先度。

運算子

描述

<< >>

陣列索引(最高優先度)

括弧其實不是運算子,但是能夠讓你明確的指定運算的優先順序。

x[index]

說明

x.attribute

屬性參照

foo()

呼叫函式

await x

等待訊號和協程

x is Node

型別轉換

另見 is_instance_of() 函式。

x ** y

力度

x 與其自身相乘 y 次,類似於呼叫 pow() 函式。

**注意:**在 GDScript 中,** 運算子是`左結合 <https://en.wikipedia.org/wiki/Operator_associativity>`_的。詳情見表後。

~x

按位元 (Bitwise) NOT(非)

+x
-x

縮 排

x * y
x / y
x % y

乘法/除法/餘數

% 運算子也用於:ref:字串的格式化 <doc_gdscript_printf>

**注意:**這些運算子的行為與 C++ 一致,對於來自 Python、JavaScript 等語言的使用者可能存在意外的行為。詳情見表後。

x + y
x - y

加法/陣列的串聯

x << y
x >> y

位元移位

x & y

按位元 AND(與)

x ^ y

按位元 XOR (互斥或)

x | y

按位元 OR(或)

x == y
x != y
x < y
x > y
x <= y
x >= y

比較

詳情見表後。

x in y
x not in y

遮擋模式

in 也在 for 關鍵字的語法中使用。

not x
!x

布林“非”,以及:ref:`不推薦使用 <boolean_operators>`的別名

x and y
x && y

布林“與”,以及:ref:`不推薦使用 <boolean_operators>`的別名

x or y
x || y

布林“或”,以及:ref:`不推薦使用 <boolean_operators>`的別名

真運算式 if 條件 else 假運算式

三元 if/else

x as Node

型別轉換

x = y
x += y
x -= y
x *= y
x /= y
x **= y
x %= y
x &= y
x |= y
x ^= y
x <<= y
x >>= y

賦值(最低優先度)

運算式中不能使用設定運算子。

備註

某些運算符的行為可能與您的預期不同:

  1. 如果「/」運算子的兩個運算元都是 int <class_int>`,則執行整數除法而不是小數除法。例如``5 / 2 == 2``,而不是``2.5``。如果不需要,請至少使用一個 float <class_float>` 文字 (x / 2.0)、cast (float(x) / y) 或乘以``1.0' (``x * 1.0 / y)。

  2. % 運算子僅適用於整數,對於浮點數則使用 fmod() <class_@GlobalScope_method_fmod> 函式。

  3. 對於負值,「%」運算子和「fmod()」使用「截斷<https://en.wikipedia.org/wiki/Truncation>」_而不是向負無窮大捨去。這意味著餘數有符號。如果您需要數學意義上的餘數,請使用:ref:posmod() <class_@GlobalScope_method_posmod> 和:ref:fposmod() <class_@GlobalScope_method_fposmod> 函式。

  4. ** 運算子是「左關聯<https://en.wikipedia.org/wiki/Operator_associativity>`_」。這意味著「2 ** 2 ** 3」等於「(2 ** 2) ** 3」。使用括號明確指定您需要的優先級,例如``2 ** (2 ** 3)``。

  5. ==!= 運算子有時允許您比較不同型別的值(例如,1 == 1.0 為 true),但在其他情況下可能會導致執行時錯誤。如果您不確定操作數的型別,可以安全地使用 is_same() <class_@GlobalScope_method_is_same>` 函式(但請注意,它對型別和參考更加嚴格)。若要比較浮點數,請使用 is_equal_approx() <class_@GlobalScope_method_is_equal_approx>` 和 is_zero_approx() <class_@GlobalScope_method_is_zero_approx>` 函式。

字面值

Example(s)

描述

null

Null value

false, true

Boolean values

45

10 進位整數

0x8f51

16 進位整數

0b101010

2 進位整數

3.1458.1e-10

浮點數(實數)

"Hello""Hi"

Regular strings

"""Hello""", '''Hi'''

Triple-quoted regular strings

r"Hello", r'Hi'

Raw strings

r"""Hello""", r'''Hi'''

Triple-quoted raw strings

&"name"

String

@"Node/Label"

NodePath

There are also two constructs that look like literals, but actually are not:

範例

描述

$NodePath

get_node("NodePath") 的簡寫

%UniqueNode

get_node("NodePath") 的簡寫

整數與浮點數可以用 _ 來分隔數字,讓數字的可讀性更高。下列這些數字格式都是有效的:

12_345_678  # Equal to 12345678.
3.141_592_7  # Equal to 3.1415927.
0x8080_0000_ffff  # Equal to 0x80800000ffff.
0b11_00_11_00  # Equal to 0b11001100.

Regular string literals can contain the following escape sequences:

逸出序列

會被解析為

\n

換行 (LF)

\t

水平 TAB 字元

\r

歸位字元

\a

警告 (警示嗶聲/鈴聲)

\b

倒退鍵

\f

Formfeed 分頁字元

\v

縱向 TAB 字元

\"

雙引號

\'

單引號

\\

反斜線

\uXXXX

Unicode 字碼指標 XXXX (16進位,不區分大小寫)

\uXXXX

Unicode 字碼指標 XXXX (16進位,不區分大小寫)

There are two ways to represent an escaped Unicode character above 0xFFFF:

此外,在字串中使用“”後跟換行符將允許您在下一行中繼續它,而無需在字串本身中插入換行符。

A string enclosed in quotes of one type (for example ") can contain quotes of another type (for example ') without escaping. Triple-quoted strings allow you to avoid escaping up to two consecutive quotes of the same type (unless they are adjacent to the string edges).

Raw string literals always encode the string as it appears in the source code. This is especially useful for regular expressions. Raw strings do not process escape sequences, but you can "escape" a quote or backslash (they replace themselves).

print("\tchar=\"\\t\"")  # Prints `    char="\t"`.
print(r"\tchar=\"\\t\"") # Prints `\tchar=\"\\t\"`.

GDScript 亦支援 GDScript 格式字串

註釋

GDScript 中有一些特殊的標記,它們的作用類似於關鍵字,但實際上並非如此,它們是*註釋*。每個註釋都以“@”字元開頭,並由名稱指定。每個註解的詳細描述和範例可以在 GDScript 類別參考 <class_@GDScript> 中找到。

註解會影響外部工具處理腳本的方式,通常不會改變行為。

例如,您可以使用它將值匯出到編輯器:

@export_range(1, 100, 1, "or_greater")
var ranged_var: int = 50

有關匯出的說明文件已移至 GDScript exported properties

任何與所需參數型別相容的常數表達式都可以作為註解參數值傳遞:

const MAX_SPEED = 120.0

@export_range(0.0, 0.5 * MAX_SPEED)
var initial_speed: float = 0.25 * MAX_SPEED

可以在每行指定一個註釋,也可以在同一行中指定所有註釋。它們影響下一個不是註釋的敘述。註釋可以將參數傳送到括號之間並用逗號分隔。

兩者是相同的:

@annotation_a
@annotation_b
var variable

@annotation_a @annotation_b var variable

“@onready”註釋

使用節點時,我們通常會想以變數來參照到場景的某個部分。由於場景只有在進入有效場景樹後才能保證有正確配置,所以在 Node._ready() 呼叫後才能取得子節點。

var my_label


func _ready():
    my_label = get_node("MyLabel")

這麼做有點麻煩,特別是當節點與外部參照很多時。為此,GDScript 中有 onready 關鍵字,會推遲成員變數的初始化,直到 _ready() 呼叫後。我們可以將上述程式碼用這樣一行程式碼來取代:

@onready var my_label = get_node("MyLabel")

警告

將“@onready”和任何“@export”註釋應用於同一變數並不像您期望的那樣工作。 @onready 註釋將導致在``@export`` 生效後設定預設值並將覆蓋它:

@export var a = "init_value_a"
@onready @export var b = "init_value_b"

func _init():
    prints(a, b) # init_value_a <null>

func _notification(what):
    if what == NOTIFICATION_SCENE_INSTANTIATED:
        prints(a, b) # exported_value_a exported_value_b

func _ready():
    prints(a, b) # exported_value_a init_value_b

因此,會產生“ONREADY_WITH_EXPORT”警告,預設將其視為錯誤。我們不建議禁用或忽略它。

注釋

# 開始到行未的所有東西都會被忽略並當作註解。

# This is a comment.

小訣竅

In the Godot script editor, special keywords are highlighted within comments to bring the user's attention to specific comments:

  • Critical (appears in red): ALERT, ATTENTION, CAUTION, CRITICAL, DANGER, SECURITY

  • Warning (appears in yellow): BUG, DEPRECATED, FIXME, HACK, TASK, TBD, TODO, WARNING

  • Notice (appears in green): INFO, NOTE, NOTICE, TEST, TESTING

These keywords are case-sensitive, so they must be written in uppercase for them to be recognized:

# In the example below, "TODO" will appear in yellow by default.
# The `:` symbol after the keyword is not required, but it's often used.

# TODO: Add more items for the player to choose from.

The list of highlighted keywords and their colors can be changed in the Text Editor > Theme > Comment Markers section of the Editor Settings.

Code regions

Code regions are special types of comments that the script editor understands as foldable regions. This means that after writing code region comments, you can collapse and expand the region by clicking the arrow that appears at the left of the comment. This arrow appears within a purple square to be distinguishable from standard code folding.

The syntax is as follows:

# Important: There must be *no* space between the `#` and `region` or `endregion`.

# Region without a description:
#region
...
#endregion

# Region with a description:
#region Some description that is displayed even when collapsed
...
#endregion

小訣竅

To create a code region quickly, select several lines in the script editor, right-click the selection then choose Create Code Region. The region description will be selected automatically for editing.

It is possible to nest code regions within other code regions.

Here's a concrete usage example of code regions:

# This comment is outside the code region. It will be visible when collapsed.
#region Terrain generation
# This comment is inside the code region. It won't be visible when collapsed.
func generate_lakes():
    pass

func generate_hills():
    pass
#endregion

#region Terrain population
func place_vegetation():
    pass

func place_roads():
    pass
#endregion

This can be useful to organize large chunks of code into easier to understand sections. However, remember that external editors generally don't support this feature, so make sure your code is easy to follow even when not relying on folding code regions.

備註

Individual functions and indented sections (such as if and for) can always be collapsed in the script editor. This means you should avoid using a code region to contain a single function or indented section, as it won't bring much of a benefit. Code regions work best when they're used to group multiple elements together.

程式碼樣式設定

GDScript 中的一行代碼可以使用反斜杠 (''') 在下一行繼續。在一行末尾添加一個,下一行上的程式碼將像反斜杠一樣。下面是一個示例:

var a = 1 + \
2

一行可以連續多次,如下圖所示:

var a = 1 + \
4 + \
10 + \
4

內建型別

內建型別按堆疊配置 (Stack-allocated) 。傳遞時只傳遞值。也就是說每次賦值或作為參數傳給函式的時候都會建立一份複製。唯一的例外是 Array (陣列)與 Dictionary (字典),這兩種型別是以參照傳遞的,所以內容共用的。(如 PoolByteArray 等的 Pooled Array 仍以值傳遞。)

基礎內建型別

GDScript 的變數可以被指派為多種內建型別。

null

null 是一個沒有包含任何資訊的空資料型別,不能指派為其他任何的值。

bool

「布林 (Boolean)」的縮寫,只會是 truefalse

int

「整數 (Interger)」的縮寫。可以保存整數(正數與負數)。可以儲存 64 位元的值,相當於 C++ 的「int64_t」。

float

使用浮點數值,儲存包含小數的實數。保存為 64 位元的數值,相當於 C++ 中的「倍精確 (double)」型別。注意:目前,如 Vector2、Vector3、與 PoolRealArray 資料結構都儲存 32 位元的單精確「float(浮點)」值。

String

A sequence of characters in Unicode format.

String

一個不可變的字串,僅允許每個名稱有一個實例。它們的建立速度較慢,並且可能會導致在多執行緒時等待鎖。作為交換,它們的比較速度非常快,這使得它們成為字典鍵的良好候選者。

NodePath

節點或節點屬性的預解析路徑。它可以輕鬆地分配給字串或從字串分配。它們可用於與樹互動以取得節點,或影響屬性,例如使用 Tweens <class_Tween>`。

內建向量型別

Vector2

2D 向量包含 xy 欄位。也能用與陣列一樣的方式存取。

Vector2

與 Vector2 相同,但分量是整數。對於表示 2D 網格中的專案很有用。

Rect2

2D 矩形包含兩個向量欄位 position (位置)與 size (大小)。也包含了一個 end 欄位,為 position + size

Vector3

3D 向量包含 xy 、與 z 欄位。也能用與陣列一樣的方式存取。

Vector3

與 Vector3 相同,但分量是整數。可用於對 3D 網格中的專案進行索引。

Transform2D

用於 2D 幾何變換的 3×2 矩陣。

Plane

包含 normal 向量欄位與 d 常數距離的標準形式的 3D 平面型別。

Quat

四元數 (Quaternion) 是一種用於表示 3D 旋轉的資料型別。進行內插旋轉時很有用。

AABB

座標軸對齊定界框 (AABB, Axis-aligned Bounding Box),或稱為 3D 框 (3D Box),包含了兩個向量欄位: position (位置)與 size (大小)。也包含了一個 end 欄位,即為 position + size

Basis

用於 3D 旋轉與縮放的 3x3 矩陣。包含了三個向量欄位( xy 、與 z ),一樣可以視為 3D 向量的陣列的來存取。

Transform2D

3D 變換包含了一個 Basis 欄位 basis 以及一個 Vector3 欄位 origin

引擎內建型別

Color

色彩資料型別包含 rgb 、與 a 欄位。也可以存取 hs 、與 v ,代表色相 (Hue) /飽和度 (Saturation)/明度 (Value)。

RID

資源 ID (RID, Resource ID)。伺服器使用通用 RID 來參照不透明資料。

Object

所有非內建型別的基礎類別。

內建容器型別

Array

可包含任意物件型別,也包含其他陣列或字典型別(詳見下方)。陣列的大小可以動態調整。陣列的索引從 0 開始。若使用負數索引則自尾端開始。

var arr = []
arr = [1, 2, 3]
var b = arr[1] # This is 2.
var c = arr[arr.size() - 1] # This is 3.
var d = arr[-1] # Same as the previous line, but shorter.
arr[0] = "Hi!" # Replacing value 1 with "Hi!".
arr.append(4) # Array is now ["Hi!", 2, 3, 4].

切線陣列

Godot 4.0 新增了對型別陣列的支援。在寫入操作中,Godot 檢查元素值是否與指定型別配對,因此陣列不能包含無效值。 GDScript 靜態分析器考慮型別化陣列,但像「front()」和「back()」這樣的陣列方法仍然具有「Variant」回傳型別。

型別化陣列的語法為“Array[Type]”,其中“Type”可以是任何“Variant”型別、本機類別、使用者類別或列舉。不支援巢狀陣列型別(如“Array[Array[int]]”)。

var a: Array[int]
var b: Array[Node]
var c: Array[MyClass]
var d: Array[MyEnum]
var e: Array[Variant]

ArrayArray[Variant] 是同一件事。

備註

陣列是透過引用傳遞的,因此陣列元素型別也是運作時變數引用的記憶體結構的屬性。變數的靜態型別限制了它可以引用的結構。因此,您**不能**為陣列指派不同的元素型別,即使該型別是所需型別的子型別。

如果您想要*轉換*型別化陣列,您可以建立一個新陣列並使用 Array.assign() 方法:

var a: Array[Node2D] = [Node2D.new()]

# (OK) You can add the value to the array because `Node2D` extends `Node`.
var b: Array[Node] = [a[0]]

# (Error) You cannot assign an `Array[Node2D]` to an `Array[Node]` variable.
b = a

# (OK) But you can use the `assign()` method instead. Unlike the `=` operator,
# the `assign()` method copies the contents of the array, not the reference.
b.assign(a)

唯一的例外是“Array”(“Array[Variant]”)型別,以方便使用者並與舊程式碼相容。但是,對無型別陣列的操作被認為是不安全的。

本地坐標

為求速度,GDScript 的陣列在記憶體中只會以線性進行分配。對於大型陣列(超過數萬元素),可能會導致記憶體片段化。若有需要考慮這類情況,可以使用其他特殊型別的陣列。這型別別只能存放單一資料型別、能避免記憶體片段化且使用更少記憶體。但這些型別具不可分割性且速度比通用陣列還來的慢。所以只推薦用於有大量資料的情況:

  • PoolByteArray :位元組的陣列(0 至 255 的整數)。

  • PoolIntArray : 整數陣列。

  • PoolIntArray : 整數陣列。

  • PoolRealArray :浮點數陣列。

  • PoolRealArray :浮點數陣列。

  • PoolStringArray :字串陣列。

  • PoolVector2ArrayVector2 物件的陣列。

  • PoolVector3ArrayVector3 物件的陣列。

  • PoolColorArrayColor 物件的陣列。

Dictionary

可以包含以獨立 Key 參照數值的關聯式容器。

var d = {4: 5, "A key": "A value", 28: [1, 2, 3]}
d["Hi!"] = 0
d = {
    22: "value",
    "some_key": 2,
    "other_key": [2, 3, 4],
    "more_key": "Hello"
}

Lua-style table syntax is also supported. Lua-style uses = instead of : and doesn't use quotes to mark string keys (making for slightly less to write). However, keys written in this form can't start with a digit (like any GDScript identifier), and must be string literals.

var d = {
    test22 = "value",
    some_key = 2,
    other_key = [2, 3, 4],
    more_key = "Hello"
}

要往現有字典新增 Key,只需要像存取現有 Key 一樣存取,然後賦值:

var d = {} # Create an empty Dictionary.
d.waiting = 14 # Add String "waiting" as a key and assign the value 14 to it.
d[4] = "hello" # Add integer 4 as a key and assign the String "hello" as its value.
d["Godot"] = 3.01 # Add String "Godot" as a key and assign the value 3.01 to it.

var test = 4
# Prints "hello" by indexing the dictionary with a dynamic key.
# This is not the same as `d.test`. The bracket syntax equivalent to
# `d.test` is `d["test"]`.
print(d[test])

備註

方括號語法不僅可用於 Dictionary,也可用來存取任何 Object 的屬性。請記得當嘗試存取不存在的屬性時會導致腳本錯誤。要避免錯誤,請使用 Object.get()Object.set() 替代。

int

訊號是物件可以向想要收聽它的人發出的訊息。訊號型別可用於傳遞發射器。

透過從實際物件獲取訊號可以更好地使用它們,例如“$Button.button_up”。

Plane

包含一個物件和一個函式,這對於將函式作為值傳遞非常有用(例如,連接到訊號時)。

取得一個方法作為成員會傳回一個可呼叫物件。 var x = $Sprite2D.rotate 會將``x`` 的值設為可呼叫的對象,其中``$Sprite2D`` 作為對象,rotate 作為方法。

您可以使用“call”方法來呼叫它:“x.call(PI)”。

資料

變數

變數可以作為類別成員或函式的區域變數存在。變數使用 var 關鍵字建立,並且可選擇性地在初始化時賦值。

var a # Data type is 'null' by default.
var b = 5
var c = 3.8
var d = b + c # Variables are always initialized in order.

變數也可選擇性地指定型別。當指定型別,變數將強制必須維持相同型別,當試著指派不相容的型別會造成錯誤。

在變數宣告時在變數名稱後使用 : (冒號)接上型別來執行型別。

var my_vector2: Vector2
var my_node: Node = Sprite2D.new()

若變數在宣告時即進行初始化,則型別會自動推定,故可省略型別名稱:

var my_vector2 := Vector2() # 'my_vector2' is of type 'Vector2'.
var my_node := Sprite2D.new() # 'my_node' is of type 'Sprite2D'.

型別推定僅可於指派的值有定義型別時發生,否則將發生錯誤。

有效的型別為:

  • 內建型別(Array、Vector2、int、String…等)。

  • 引擎類別(Node、Resource、Reference…等)。

  • 常數名稱,若該常數包含腳本資源(若宣告 const MyScript = preload("res://my_script.gd") 則可使用 MyScript )。

  • 在相同腳本內的其他類別,尊重作用域( InnerClass.NestedClass 若在 class InnerClass 內宣告 class NextedClass 則為相同作用域)。

  • 使用 class_name 關鍵字宣告的腳本類別。

  • 自動加載註冊為單例。

備註

雖然「Variant」是有效的型別規範,但它不是實際的型別。它僅意味著沒有設定型別,相當於根本沒有靜態型別。因此,預設情況下不允許對“Variant”進行推理,因為這可能是錯誤。

您可以透過在專案設定中變更它來關閉此檢查,或將其僅作為警告。有關詳細信息,請參閱 doc_gdscript_warning_system`。

本地坐標

類別成員變數可以宣告為 static:

static var a

靜態變數屬於類,而不屬於實例。這意味著靜態變數在多個實例之間共享值,這與常規成員變數不同。

在類別內部,您可以從任何函式(靜態和非靜態)存取靜態變數。從類別外部,您可以使用類別或實例存取靜態變數(不建議使用第二種,因為它的可讀性較差)。

備註

“@export” 和 “@onready” 註解不能應用於靜態變數。局部變數不能是靜態的。

以下範例定義了一個具有名為“max_id”的靜態變數的“Person”類別。我們在“_init()”函式中增加“max_id”。這使得我們可以輕鬆追蹤遊戲中「Person」實例的數量。

# person.gd
class_name Person

static var max_id = 0

var id
var name

func _init(p_name):
    max_id += 1
    id = max_id
    name = p_name

在這段程式碼中,我們建立了「Person」類別的兩個實例,並檢查該類別和每個實例是否具有相同的「max_id」值,因為該變數是靜態的並且每個實例都可以存取。

# test.gd
extends Node

func _ready():
    var person1 = Person.new("John Doe")
    var person2 = Person.new("Jane Doe")

    print(person1.id) # 1
    print(person2.id) # 2

    print(Person.max_id)  # 2
    print(person1.max_id) # 2
    print(person2.max_id) # 2

靜態變數可以有型別提示、setter 和 getter:

static var balance: int = 0

static var debt: int:
    get:
        return -balance
    set(value):
        balance = -value

基底類別靜態變數也可以透過子類別別存取:

class A:
    static var x = 1

class B extends A:
    pass

func _ready():
    prints(A.x, B.x) # 1 1
    A.x = 2
    prints(A.x, B.x) # 2 2
    B.x = 3
    prints(A.x, B.x) # 3 3

“@static_unload”註釋

由於 GDScript 類別是資源,因此即使沒有該類別的更多實例並且沒有留下任何其他引用,腳本中包含靜態變數也會阻止其被解除安裝。如果靜態變數儲存大量資料或保存對其他專案資源(例如場景)的引用,這一點可能很重要。您應該手動清理這些資料,或者如果靜態變數不儲存重要資料並且可以重設,則使用 @static_unload <class_@GDScript_annotation_@static_unload>` 註解。

警告

目前,由於錯誤,即使使用“@static_unload”註釋,腳本也永遠不會被釋放。

請注意,「@static_unload」適用於整個腳本(包括內部類別),並且必須放置在腳本的頂部,在「class_name」和「extends」之前:

@static_unload
class_name MyNode
extends Node

另請參閱“靜態函式”和“靜態建構函式”。

型別轉換

當指派值給有型別的變數時,該值必須有相容的型別。若有需要強制轉換值為特定型別,特別是物件型別時,可以使用型別轉換運算子 as

在物件型別間進行型別轉換時,若值為相同型別或該轉換型別的子型別時,取得的結果將為相同型別。

var my_node2D: Node2D
my_node2D = $Sprite2D as Node2D # Works since Sprite2D is a subtype of Node2D.

若該值非子型別,則轉換操作會導致 null 值。

var my_node2D: Node2D
my_node2D = $Button as Node2D # Results in 'null' since a Button is not a subtype of Node2D.

對於內建型別,若可能的話將強制轉換,否則引擎將產生錯誤。

var my_int: int
my_int = "123" as int # The string can be converted to int.
my_int = Vector2() as int # A Vector2 can't be converted to int, this will cause an error.

當與場景樹互動時,型別轉換對於取得更加的型別安全 (Type-Safe) 變數也很有用:

# Will infer the variable to be of type Sprite2D.
var my_sprite := $Character as Sprite2D

# Will fail if $AnimPlayer is not an AnimationPlayer, even if it has the method 'play()'.
($AnimPlayer as AnimationPlayer).play("walk")

常數

常數是一種無法在遊戲執行時更改的數值。常數的值必須在編譯時期就已定好。使用 const 關鍵字即可為常數設定名稱。若在常數定義後試圖為常數賦值會回傳錯誤。

我們建議,當有不會更改的值時,一概使用常數。

const A = 5
const B = Vector2(20, 20)
const C = 10 + 20 # Constant expression.
const D = Vector2(20, 30).x # Constant expression: 20.
const E = [1, 2, 3, 4][0] # Constant expression: 1.
const F = sin(20) # 'sin()' can be used in constant expressions.
const G = x + 20 # Invalid; this is not a constant expression!
const H = A + 20 # Constant expression: 25 (`A` is a constant).

雖然常數的型別時依據指派的值來推定的,但也可以加上明確的型別指定:

const A: int = 5
const B: Vector2 = Vector2()

當指派的值的型別不相容時將產生錯誤。

您也可以在函式內建立常數,這對於命名局部魔術值很有用。

備註

由於陣列與字典是以參照來傳遞的,因此常數為「扁平」的。這表示當宣告常數型陣列或字典,之後依然可以修改其內容。只是這個常數之後不能指派為其他值而已。

Enum 列舉型別

Enum 基本上是常數的簡寫,當需要給一些常數指派連續的整數時滿有用的。

enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}

# Is the same as:
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3

若給 Enum 指派名稱,則會將所有的鍵都放入以該名稱為名的字典常數內。

重要

在 Godot 3.1 與之後的版本中,在有名稱的 Enum 中的鍵並不會註冊為全域常數。存取時必須要在前面加上 Enum 的名稱( Name.KEY )。請參考下方範例。

enum State {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT}

# Is the same as:
const State = {STATE_IDLE = 0, STATE_JUMP = 5, STATE_SHOOT = 6}

func _ready():
    # Access values with Name.KEY, prints '5'
    print(State.STATE_JUMP)
    # Use constant dictionary functions
    # prints '["STATE_IDLE", "STATE_JUMP", "STATE_SHOOT"]'
    print(State.keys())

函式

函式必須屬於一個 方法 。尋找變數的作用域優先度如下:區域 -> 類別成員 -> 全域。 self 變數永遠可用,為一個用來存取類別成員的選項,但並非強制必須使用(且與 Python 不同,GDScript 裡 不可 傳送為函式的第一個引數)。

func my_function(a, b):
    print(a)
    print(b)
    return a + b  # Return is optional; without it 'null' is returned.

函式可以於任何時機 return 。預設的回傳值為 null

如果一個函式只包含一行程式碼,可以寫在一行上:

func square(a): return a * a

func hello_world(): print("Hello World")

func empty_function(): pass

函式也可以給參數與回傳值指定型別。參數的型別可用與變數類似的方法指定:

func my_function(a: int, b: String):
    pass

若函式參數有預設值,則可推斷型別:

func my_function(int_arg := 42, String_arg := "string"):
    pass

函式的回傳值可在參數列表後以箭頭符記 (->) 來指定:

func my_int_function() -> int:
    return 0

有回傳型別的函式 必須 回傳適當的值。將型別設為 void 則表示函式不回傳任何東西。Void 函式可以使用 return 關鍵字來提早回傳,但無法回傳任何值。

func void_function() -> void:
    return # Can't return a value.

備註

非 Void 函式必須 總是 回傳一個值。若程式碼中有分歧陳述式(如 if/else 結構),則所有可能的路徑都必須要有 return。如,若在 if 內有 returnif 後卻沒有,則編輯器會產生錯誤,因為若區塊程式碼未被執行,則該函式將不會有有效的回傳值。

函式參照

就 Callable <class_Callable> 物件而言,函式是一流的專案。透過名稱引用函式而不呼叫它會自動產生正確的可呼叫函式。這可用於將函式作為參數傳遞。

func map(arr: Array, function: Callable) -> Array:
    var result = []
    for item in arr:
        result.push_back(function.call(item))
    return result

func add1(value: int) -> int:
    return value + 1;

func _ready() -> void:
    var my_array = [1, 2, 3]
    var plus_one = map(my_array, add1)
    print(plus_one) # Prints [2, 3, 4].

備註

可呼叫物件**必須**使用“call”方法進行呼叫。您不能直接使用``()`` 運算子。實作此行為是為了避免直接函式呼叫的效能問題。

隨機數函式

Lambda 函式可讓您宣告不屬於類別的函式。相反,會建立 Callable <class_Callable>` 物件並將其直接指派給變數。這對於建立可傳遞的可呼叫物件而不污染類別範圍非常有用。

var lambda = func(x): print(x)
lambda.call(42) # Prints "42"

可以出於除錯目的命名 Lambda 函式::

var lambda = func my_lambda(x):
    print(x)

Lambda 函式捕獲本機環境。局部變數按值傳遞,因此如果在局部函式中更改,它們不會在 lambda 中更新:

var x = 42
var my_lambda = func(): print(x)
my_lambda.call() # Prints "42"
x = "Hello"
my_lambda.call() # Prints "42"

備註

外部作用域的值的行為類似於常數。因此,如果宣告一個陣列或字典,之後仍然可以對其進行修改。

靜態函式

函式可以宣告為靜態。當函式被宣告為靜態,將無法存取實體的成員變數或 self 。主要適用於製作函式庫或 Helper 函式:

static func sum2(a, b):
    return a + b

Lambda 不能宣告為靜態。

另請參閱“靜態變數”和“靜態建構函式”。

稱述句與流程控制

稱述句為標準,可以為複製、函式呼叫、流程結構…等(相見下方)。 作為稱述句分隔字元的 ; 是完全可選的。

Expression Node 運算式節點

表達式是依序排列的運算子及其運算元的序列。表達式本身也可以是敘述,但只有呼叫才可以合理地用作敘述,因為其他表達式沒有副作用。

表達式傳回可指派給有效目標的值。某些運算符的操作數可以是另一個表達式。賦值不是表達式,因此不傳回任何值。

Godot 沒有使用限制

2 + 2 # Binary operation.
-5 # Unary operation.
"okay" if x > 4 else "not okay" # Ternary operation.
x # Identifier representing variable or constant.
x.a # Attribute access.
x[4] # Subscript access.
x > 2 or x < 5 # Comparisons and logic operators.
x == y + 2 # Equality test.
do_something() # Function call.
[1, 2, 3] # Array definition.
{A = 1, B = 2} # Dictionary definition.
preload("res://icon.png") # Preload builtin function.
self # Reference to current instance.

標識符、屬性和下標是有效的賦值目標。其他表達式不能位於賦值的左側。

if/else/elif

簡單的條件判斷可以使用 if/else/elif 語法來建立。可以在條件周圍加上括號,但並非必要。由於基於 Tab 排版的性質,可以使用 elif 代替 else/if 來維持縮排的等級。

if (expression):
    statement(s)
elif (expression):
    statement(s)
else:
    statement(s)

較短的陳述式可以與條件寫在同一行內:

if 1 + 1 == 2: return 2 + 2
else:
    var x = 3 + 3
    return x

有時候我們會想依據布林表達式來指派不同的初始值。這時候可以使用三元表達式:

var x = (value) if (expression) else (value)
y += 3 if y < 10 else -1

可以通過巢狀三元 if 運算式來處理的超過兩種可能性的情況。巢狀時,推薦把三元 if 運算式拆分到多行以保持可讀性:

var count = 0

var fruit = (
        "apple" if count == 2
        else "pear" if count == 1
        else "banana" if count == 0
        else "orange"
)
print(fruit)  # banana

# Alternative syntax with backslashes instead of parentheses (for multi-line expressions).
# Less lines required, but harder to refactor.
var fruit_alt = \
        "apple" if count == 2 \
        else "pear" if count == 1 \
        else "banana" if count == 0 \
        else "orange"
print(fruit_alt)  # banana

你可能還想要檢查某個值是否包含在某些東西之中。可以通過 if 敘述與 in 運算子的組合來實作:

# Check if a letter is in a string.
var text = "abc"
if 'b' in text: print("The string contains b")

# Check if a variable is contained within a node.
if "varName" in get_parent(): print("varName is defined in parent!")

while

可以使用 while 語法來建立簡單的迴圈。迴圈可以使用 break 來中斷或是使用 continue 來繼續:

while (expression):
    statement(s)

for

要在如陣列或表格中的一段範圍內迭代,可以使用 for 迴圈。當在陣列中迭代時,目前的陣列元素會儲存於迴圈變數中。當於字典中迭代時,保存在迴圈變數內的則會是 索引

for x in [5, 7, 11]:
    statement # Loop iterates 3 times with 'x' as 5, then 7 and finally 11.

var dict = {"a": 0, "b": 1, "c": 2}
for i in dict:
    print(dict[i]) # Prints 0, then 1, then 2.

for i in range(3):
    statement # Similar to [0, 1, 2] but does not allocate an array.

for i in range(1, 3):
    statement # Similar to [1, 2] but does not allocate an array.

for i in range(2, 8, 2):
    statement # Similar to [2, 4, 6] but does not allocate an array.

for i in range(8, 2, -2):
    statement # Similar to [8, 6, 4] but does not allocate an array.

for c in "Hello":
    print(c) # Iterate through all characters in a String, print every letter on new line.

for i in 3:
    statement # Similar to range(3).

for i in 2.2:
    statement # Similar to range(ceil(2.2)).

如果要在迭代陣列時為其賦值,最好使用「for i in array.size()」。

for i in array.size():
        array[i] = "Hello World"

迴圈變數是 for 迴圈的局部變量,為其賦值不會改變陣列上的值。透過引用傳遞的物件(例如節點)仍然可以透過呼叫循環變數上的方法來操作。

for string in string_array:
    string = "Hello World" # This has no effect

for node in node_array:
    node.add_to_group("Cool_Group") # This has an effect

match

match 陳述式用於在程式內分歧執行。與其他許多語言內的 switch 表達式相同,但有些額外的功能。

警告

“match” 比“==” 運算符的型別更嚴格。例如“1”將**不**配對“1.0”。唯一的例外是 StringStringName 配對:例如,字串 "hello" 被認為等於 StringName &"hello"

Basic syntax
match <expression>:
    <pattern(s)>:
        <block>
    <pattern(s)> when <guard expression>:
        <block>
    <...>
Crash-course for people who are familiar with switch statements
  1. switch 取代為 match

  2. 移除 case

  3. 移除 case

  4. default 改為底線。

Control flow

搜尋模式會按照由上到下的順序來配對。若與搜尋模式相符,則會執行第一個對應的區塊。之後會繼續執行 match 陳述式下方的程式。若想往下執行,可以使用 continue 來停止執行目前的區塊,並往下搜尋其他符合的搜尋模式。

備註

3.x 中支援的「match」中特殊的「繼續」行為在 Godot 4.0 中被刪除。

The following pattern types are available:

  • Literal pattern

    Matches a literal:

    match x:
        1:
            print("We are number one!")
        2:
            print("Two are better than one!")
        "test":
            print("Oh snap! It's a string!")
    
  • Expression pattern

    Matches a constant expression, an identifier, or an attribute access (A.B):

    match typeof(x):
        TYPE_FLOAT:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • 萬用字元

    尋找所有東西。寫成一個底線。

    用法與其他語言中 switchdefault 相同:

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        _:
            print("It's not 1 or 2. I don't care to be honest.")
    
  • 繫結

    繫結搜尋模式使用一個新變數。與萬用字元模式一樣會配對到所有東西,同時將該值指派給一個變數。對於陣列或字典模式很有用:

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        var new_var:
            print("It's not 1 or 2, it's ", new_var)
    
  • 陣列

    搜尋陣列。陣列中的每一個元素也都是搜尋模式,可以巢狀使用。

    會先檢查陣列的長度,長度必須與搜尋模式相同,否則將不會視為相符。

    開放結尾陣列 :通過將最後一個子搜尋模式設為 .. 可配對大於模式的陣列。

    所有子搜尋模式都以逗號分隔。

    match x:
        []:
            print("Empty array")
        [1, 3, "test", null]:
            print("Very specific array")
        [var start, _, "test"]:
            print("First element is ", start, ", and the last is \"test\"")
        [42, ..]:
            print("Open ended array")
    
  • 字典

    與陣列模式相同。所有索引鍵都必須為常數搜尋模式。

    會先檢查字典的大小,必須與搜尋模式相同,否則將不會視為相符。

    開放結尾字典 :通過將最後一個子搜尋模式設為 .. 可允許配對大於搜尋模式的字典。

    所有子模式都必須以逗號分隔。

    若不指定值,則只會檢查索引鍵是否存在。

    值搜尋模式使用 : 來與索引值搜尋模式區分。

    match x:
        {}:
            print("Empty dict")
        {"name": "Dennis"}:
            print("The name is Dennis")
        {"name": "Dennis", "age": var age}:
            print("Dennis is ", age, " years old.")
        {"name", "age"}:
            print("Has a name and an age, but it's not Dennis :(")
        {"key": "godotisawesome", ..}:
            print("I only checked for one entry and ignored the rest")
    
  • 多重搜尋模式

    也可以逗號區分來指定多個搜尋模式。這些搜尋模式中不允許有任何繫結。

    match x:
        1, 2, 3:
            print("It's 1 - 3")
        "Sword", "Splash potion", "Fist":
            print("Yep, you've taken damage")
    
Pattern guards

Only one branch can be executed per match. Once a branch is chosen, the rest are not checked. If you want to use the same pattern for multiple branches or to prevent choosing a branch with too general pattern, you can specify a guard expression after the list of patterns with the when keyword:

match point:
    [0, 0]:
        print("Origin")
    [_, 0]:
        print("Point on X-axis")
    [0, _]:
        print("Point on Y-axis")
    [var x, var y] when y == x:
        print("Point on line y = x")
    [var x, var y] when y == -x:
        print("Point on line y = -x")
    [var x, var y]:
        print("Point (%s, %s)" % [x, y])
  • If there is no matching pattern for the current branch, the guard expression is not evaluated and the patterns of the next branch are checked.

  • If a matching pattern is found, the guard expression is evaluated.

    • If it's true, then the body of the branch is executed and match ends.

    • If it's false, then the patterns of the next branch are checked.

類別

預設情況下,所有的腳本檔案都是沒有命名的類別。此時,要參照這些類別的唯一方法就是使用相對路徑或絕對路徑的檔案位置。如,若腳本檔案命名為 character.gd

# Inherit from 'character.gd'.

extends "res://path/to/character.gd"

# Load character.gd and create a new node instance from it.

var Character = load("res://path/to/character.gd")
var character_node = Character.new()

將腳本註冊為類別

但也可以設定類別名稱,並將該類別註冊為 Godot 編輯器中的新型別。使用 class_name 來設定類別名稱。也可以選擇性地在後方加上逗號與一個圖片路徑,該圖片會用作圖示。新定義的類別與圖示會在編輯器中顯示:

# item.gd

@icon("res://interface/icons/item.png")
class_name Item
extends Node
../../../_images/class_name_editor_register_example.png

下列為類別檔案範例:

# Saved as a file named 'character.gd'.

class_name Character


var health = 5


func print_health():
    print(health)


func print_this_script_three_times():
    print(get_script())
    print(ResourceLoader.load("res://character.gd"))
    print(Character)

如果你也想使用“extends”,你可以將它們放在同一行:

class_name MyNode extends Node

備註

Godot 的類別語法很簡短,類別中只有成員變數與成員函式。函式可以為靜態函式,但變數則不可定義為靜態。同樣地,Godot 會在每次實體化時都初始化變數,包含陣列與字典。這樣一來能讓腳本在使用者不知情的情況下於不同的執行續中初始化,這正是執行緒安全的精神。

繼承

類別 (儲存為檔案) 可以繼承自:

  • 全域類別。

  • 另一個類別檔案。

  • 在另一個類別檔案中的內部類別。

無法多重繼承。

使用 extends 關鍵字來繼承:

# Inherit/extend a globally available class.
extends SomeClass

# Inherit/extend a named class file.
extends "somefile.gd"

# Inherit/extend an inner class in another file.
extends "somefile.gd".SomeInnerClass

備註

如果沒有明確定義繼承,則該類別將預設繼承:ref:class_RefCounted

可使用 is 關鍵字來檢查某個類別是否繼承了指定的實體:

# Cache the enemy class.
const Enemy = preload("enemy.gd")

# [...]

# Use 'is' to check inheritance.
if entity is Enemy:
    entity.apply_damage()

若要呼叫 母類別 中的函式 (也就是目前類別 extend 的類別),可在函式名稱前加上 .

super(args)

當目前類別定義了與母類別中相同名稱的函式時此方法特別適用。若想呼叫母類別中的方法則可在前面加上 . (類似其他語言中的 super 關鍵字):

func some_func(x):
    super(x) # Calls the same function on the super class.

如果需要呼叫超類別中不同的函式,可以用屬性運算子指定函式名稱:

func overriding():
    return 0 # This overrides the method in the base class.

func dont_override():
    return super.overriding() # This calls the method as defined in the base class.

警告

常見的誤解之一是試圖覆寫*非虛擬*引擎方法,例如「get_class()」、「queue_free()」等。由於技術原因,不支援此方法。

在Godot 3中,您可以在GDScript中「隱藏」引擎方法,如果您在GDScript中呼叫此方法,它將起作用。但是,如果在某些事件中在引擎內部呼叫該方法,引擎將**不會**執行您的程式碼。

在 Godot 4 中,即使是陰影也可能不總是有效,因為 GDScript 優化了本機方法呼叫。因此,我們新增了“NATIVE_METHOD_OVERRIDE”警告,預設將其視為錯誤。我們強烈建議不要禁用或忽略該警告。

請注意,這不適用於諸如“_ready()”、“_process()”等虛擬方法(在檔案中以“virtual”限定符標記,並且名稱以下劃線開頭)。這些方法專門用於自訂引擎行為,並且可以在 GDScript 中覆寫。訊號和通知也可用於這些目的。

類別建置函式

在類別實例化時所呼叫的類別建構函式名為“_init”。如果要呼叫基底類別建構函式,也可以使用``super``語法。請注意,每個類別都有一個始終被呼叫的隱式建構函式(定義類別變數的預設值)。 super 用於呼叫明確建構子:

func _init(arg):
   super("some_default", arg) # Call the custom base constructor.

通過範例能更好理解,來看看下面這個情況:

# state.gd (inherited class).
var entity = null
var message = null


func _init(e=null):
    entity = e


func enter(m):
    message = m


# idle.gd (inheriting class).
extends "state.gd"


func _init(e=null, m=null):
    super(e)
    # Do something with 'e'.
    message = m

還有幾件事需要注意:

  1. 若被繼承類別 (State.gd) 定義了需要參數的建置函式 _init (此例中為 e) ,則繼承類別 (Idle.gd) 必須 也定義 _init 並將適當的參數傳遞從 State.gd 中傳遞給 _init

  2. Idle.gd 的參數數量可與母類別 State.gd 不同。

  3. 在上方的例子中,傳遞給 State.gd 建置函式的 e 與傳遞給 Idle.gde 相同。

  4. Idle.gd 中的 _init 建置函式只接受 0 個參數,則即使沒有效果,依然需要傳入數值給 State.gd 母類別。也就是說,不只可將變數傳給母類別的建置函式,也可傳遞字面值,如下:

    # idle.gd
    
    func _init():
        super(5)
    

類別建置函式

靜態建構子是一個靜態函式“_static_init”,在靜態變數初始化後載入類別時自動呼叫:

static var my_static_var = 1

static func _static_init():
    my_static_var = 2

靜態建構函式不能接受參數,也不能傳回任何值。

內類別

類別檔可以再包含內類別。內類別使用 class 關鍵字來定義。這些內部類別使用 類別名稱.new() 函式來實體化。

# Inside a class file.

# An inner class in this class file.
class SomeInnerClass:
    var a = 5


    func print_value_of_a():
        print(a)


# This is the constructor of the class file's main class.
func _init():
    var c = SomeInnerClass.new()
    c.print_value_of_a()

以類別當作資源

保存為檔案的類別可視為 資源 。這些類別必須從硬碟中載入後才可在其他類別中存取。可使用 loadpreload 函式來載入(詳見下方)。通過呼叫類別物件上的 new 方法來實體化載入的類別:

# Load the class resource when calling load().
var MyClass = load("myclass.gd")

# Preload the class only once at compile time.
const MyClass = preload("myclass.gd")


func _init():
    var a = MyClass.new()
    a.some_function()

匯出

備註

有關匯出的說明文件已移至 GDScript exported properties

屬性 (Set/Get)

若能知道類別成員變數何時更改與為何更改可能很有用,我們也可能會希望以某種方法來封裝成員變數的存取。

為此,GDScript 提供了一個特殊的語法,可以在變數宣告後使用「set」和「get」關鍵字來定義屬性。然後,您可以定義一個程式碼區塊,該程式碼區塊將在存取或指派變數時執行。

範例:

var milliseconds: int = 0
var seconds: int:
    get:
        return milliseconds / 1000
    set(value):
        milliseconds = value * 1000

備註

Unlike setget in previous Godot versions, the properties setter and getter are always called (except as noted below), even when accessed inside the same class (with or without prefixing with self.). This makes the behavior consistent. If you need direct access to the value, use another variable for direct access and make the property code use that name.

Alternative syntax

Also there is another notation to use existing class functions if you want to split the code from the variable declaration or you need to reuse the code across multiple properties (but you can't distinguish which property the setter/getter is being called for):

var my_prop:
    get = get_my_prop, set = set_my_prop

This can also be done in the same line:

var my_prop: get = get_my_prop, set = set_my_prop

The setter and getter must use the same notation, mixing styles for the same variable is not allowed.

備註

You cannot specify type hints for inline setters and getters. This is done on purpose to reduce the boilerplate. If the variable is typed, then the setter's argument is automatically of the same type, and the getter's return value must match it. Separated setter/getter functions can have type hints, and the type must match the variable's type or be a wider type.

When setter/getter is not called

When a variable is initialized, the value of the initializer will be written directly to the variable. Including if the @onready annotation is applied to the variable.

使用變數的名稱將其設定在自己的 setter 中或將其獲取到其自己的 getter 中將直接存取底層成員,因此它不會產生無限遞歸並避免您明確宣告另一個變數:

signal changed(new_value)
var warns_when_changed = "some value":
    get:
        return warns_when_changed
    set(value):
        changed.emit(value)
        warns_when_changed = value

This also applies to the alternative syntax:

var my_prop: set = set_my_prop

func set_my_prop(value):
    my_prop = value # No infinite recursion.

警告

The exception does not propagate to other functions called in the setter/getter. For example, the following code will cause an infinite recursion:

var my_prop:
    set(value):
        set_my_prop(value)

func set_my_prop(value):
    my_prop = value # Infinite recursion, since `set_my_prop()` is not the setter.

工具模式

預設情況下,腳本並不會在編輯器中執行,且只能更改匯出的屬性。而某些情況下,我們會想在編輯器中執行腳本(只要這些腳本不會執行遊戲程式碼或手動避免執行遊戲腳本即可)。為此,可在腳本檔案的頂部加上 @tool 關鍵字:

@tool
extends Button

func _ready():
    print("Hello")

請參考 在編輯器中運作程式碼 以瞭解詳情。

警告

在工具腳本中以 queue_free()free() 釋放節點時請特別謹慎(特別是該節點為腳本擁有者時)。由於工具腳本會在編輯器中執行程式碼,若錯誤使用這些方法可能會使編輯器當掉。

記憶體管理

Godot 實作參考計數來釋放某些不再使用的實例,而不是垃圾收集器,或需要純手動管理。 class_RefCounted 類別的任何實例(或繼承它的任何類,例如 class_Resource )在不再使用時會自動釋放。對於任何不是 class_RefCounted 的類別的實例(例如 class_Node 或基底類別 class_Object 型別),它將保留在記憶體中,直到使用 free 刪除它()``(或節點的``queue_free()``)。

備註

如果透過 free() 或queue_free() 刪除 class_Node,它的所有子節點也會被遞歸刪除。

為了避免無法釋放的參考循環,提供了一個 class_WeakRef 函式來建立弱引用,它允許存取物件而不阻止 class_RefCounted 釋放。這是一個例子:

extends Node

var my_file_ref

func _ready():
    var f = FileAccess.open("user://example_file.json", FileAccess.READ)
    my_file_ref = weakref(f)
    # the FileAccess class inherits RefCounted, so it will be freed when not in use

    # the WeakRef will not prevent f from being freed when other_node is finished
    other_node.use_file(f)

func _this_is_called_later():
    var my_file = my_file_ref.get_ref()
    if my_file:
        my_file.close()

或者,不適用參照時,亦可使用 is_instance_valid(實體) 來判斷一個物件是否已被釋放。

訊號

訊號是用來從物件發送可讓其他物件做出反應的一項工具。若要為類別建立自定訊號,請使用 signal 關鍵字。

extends Node


# A signal named health_depleted.
signal health_depleted

備註

訊號是一種 回呼 機制。訊號也充當了 Observer 角色 (一種常見的程式設計模式)。更多資訊請參考 Game Programming Patterns 電子書中的 Observer tutorial (英語)

可通過與內建節點訊號 (如 Buttonclass_RigidBody ) 相同的方法來將自定訊號連接至方法。

在下方的例子中,我們將將 health_depleted 訊號從 Character 節點連接至 Game 節點。當 Character 節點送出訊號時會呼叫 Game 節點的 _on_Character_health_depleted

# game.gd

func _ready():
    var character_node = get_node('Character')
    character_node.health_depleted.connect(_on_character_health_depleted)


func _on_character_health_depleted():
    get_tree().reload_current_scene()

也可以與訊號一起送出任意數量的參數。

下列範例說明了如何有效使用這個功能。假設螢幕上有一個血槽,可以動畫顯示生命值的改變,但同時我們也想在場景樹中將使用者界面與玩家分開來。

Character.gd 腳本中,我們定義了 health_changed 訊號,並使用 Object.emit_signal() 送出訊號。而在場景樹中更高的 Game 節點中,我們使用 Object.connect() 方法來將訊號與 LifeBar 連接起來:

# character.gd

...
signal health_changed


func take_damage(amount):
    var old_health = health
    health -= amount

    # We emit the health_changed signal every time the
    # character takes damage.
    health_changed.emit(old_health, health)
...
# lifebar.gd

# Here, we define a function to use as a callback when the
# character's health_changed signal is emitted.

...
func _on_Character_health_changed(old_value, new_value):
    if old_value > new_value:
        progress_bar.modulate = Color.RED
    else:
        progress_bar.modulate = Color.GREEN

    # Imagine that `animate` is a user-defined function that animates the
    # bar filling up or emptying itself.
    progress_bar.animate(old_value, new_value)
...

我們在 Game 中同時取得了 CharacterLifebar 節點,然後將送出訊號的 Characeter 連接到接收器,也就使本例中的 Lifebar 節點。

# game.gd

func _ready():
    var character_node = get_node('Character')
    var lifebar_node = get_node('UserInterface/Lifebar')

    character_node.health_changed.connect(lifebar_node._on_Character_health_changed)

這樣一來便能讓 Lifebar 對生命值的更改做出反應而無需與 Character 節點耦合。

可於訊號定義後方的括號中填寫可選的參數名稱:

# Defining a signal that forwards two arguments.
signal health_changed(old_value, new_value)

這些參數會在編輯器的節點 Dock 中顯示,Godot 會使用這些參數來產生回呼函式。但,送出訊號時還是可以送出任意數量的參數,可自行決定是否要送出正確數量的引數。

../../../_images/gdscript_basics_signals_node_tab_1.png

在 GDScript 中可繫結陣列來連接訊號與方法。訊號送出後,回呼函式會收到綁定的值。每個連線繫結的參數都是獨立的,參數值則會保持不變。

當無法在送出的訊號中取得所有需要的資料時,可以使用這個陣列來為連接加上額外的常數資訊。

以上方的範例為基礎,假設我們需要在螢幕上顯示各個角色收到傷害的記錄,如 Player1 受到了 22 點傷害 之類的。 health_changed 訊號並未提供收到傷害的角色名稱,所以我們可以在連接訊號至遊戲界面時將角色名稱放在繫結的陣列參數上:

# game.gd

func _ready():
    var character_node = get_node('Character')
    var battle_log_node = get_node('UserInterface/BattleLog')

    character_node.health_changed.connect(battle_log_node._on_Character_health_changed, [character_node.name])

BattleLog 節點會接收繫結陣列中的所有元素作為額外參數:

# battle_log.gd

func _on_Character_health_changed(old_value, new_value, character_name):
    if not new_value <= old_value:
        return

    var damage = old_value - new_value
    label.text += character_name + " took " + str(damage) + " damage."

找出原始內容

await 關鍵字可以用來建立`協程 <https://zh.wikipedia.org/wiki/%E5%8D%8F%E7%A8%8B>`_,會等待某個訊號發出之後再繼續執行。對訊號或者對同為協程的函式呼叫使用 await 關鍵字會立即將控制權返回給呼叫方。發出訊號時(或者呼叫的協程完成時),就會從停止的地方繼續執行。

例如,要停止到使用者按下某個按鈕,你就可以這樣:

func wait_confirmation():
    print("Prompting user")
    await $Button.button_up # Waits for the button_up signal from Button node.
    print("User confirmed")
    return true

此時 wait_confirmation 就會變成協程,因此呼叫方也需要對它進行 await:

func request_confirmation():
    print("Will ask the user")
    var confirmed = await wait_confirmation()
    if confirmed:
        print("User confirmed")
    else:
        print("User cancelled")

請注意,請求協程的返回值時不帶 await 會觸發報告有錯:

func wrong():
    var confirmed = wait_confirmation() # Will give an error.

但是如果你不需要結果,直接非同步呼叫就可以了,既不會阻止運作,也不會讓目前的函式變成協程:

func okay():
    wait_confirmation()
    print("This will be printed immediately, before the user press the button.")

如果對不是訊號和協程的運算式使用 await,就會立即返回對應的值,函式也不會將控制權轉交回呼函式用方:

func no_wait():
    var x = await get_five()
    print("This doesn't make this function a coroutine.")

func get_five():
    return 5

這就意味著,如果從不是協程的函式中返回訊號,那麼呼叫方就會等待那個訊號:

func get_signal():
    return $Button.button_up

func wait_button():
    await get_signal()
    print("Button was pressed")

備註

與之前版本 Godot 中的 yield 不同,你無法獲取函式狀態物件。這是出於型別安全的考慮。要實作型別安全,函式就沒法說自己在返回 int 的同時還可能在運作時返回函式狀態物件。

Assert 關鍵字

assert 關鍵字可以用來在除錯建置中檢查條件。這些判斷提示會在非除錯建置中忽略。這表示作為參數傳遞的運算式在以發行模式匯出的專案中將不會被計算。因此,判斷提示 不可 包含有副作用的運算式。否則,腳本會因為專案是否於除錯建置中而有不同的行為。

# Check that 'i' is 0. If 'i' is not 0, an assertion error will occur.
assert(i == 0)

若從編輯器中執行專案,則專案會在判斷提示發生錯誤時暫停。

你還可以傳入自訂的錯誤消息,會在判斷提示失敗時顯示:

assert(enemy_power < 256, "Enemy is too powerful!")