GDScript basics

Введение

GDScript is a high-level, dynamically typed programming language used to create content. It uses a syntax similar to Python (blocks are indent-based and many keywords are similar). Its goal is to be optimized for and tightly integrated with Godot Engine, allowing great flexibility for content creation and integration.

История

In the early days, the engine used the Lua scripting language. Lua is fast, but creating bindings to an object oriented system (by using fallbacks) was complex and slow and took an enormous amount of code. After some experiments with Python, it also proved difficult to embed.

Последним сторонним скриптовым языком, использованным для создания игр был Squirrel, от которого также пришлось отказаться. В этот момент стало очевидно, что для особой архитектуры Godot самым оптимальным вариантом будет использование своего собственного скриптового языка:

  • Godot встраивает скрипты в узлы. Большинство языков не предназначены для этого.
  • Godot использует несколько встроенных типов данных для 2D и 3D математики. Скриптовые языки не предоставляют такого функционала, поэтому привязывать их неэффективно.
  • Godot очень активно использует потоки для считывания и инициализации данных из сети или с диска. Скриптовые интерпретаторы популярных языков не очень хорошо дружат с этим.
  • Godot уже имеет модель управления памятью для ресурсов, а большинство скриптовых языков предоставляют свою собственную модель, что приводит к двойной работе и множеству багов.
  • Код привязки всегда неряшливый и приводит к некоторым ошибкам, неожиданным багам и, как правило, низкой поддержке.

Результатом этих рассуждений является GDScript. Язык и интерпретатор для GDScript оказался меньше чем код привязки для Lua и Squirrel, имея при этом такую же функциональность. Со временем наличие встроенного языка оказалось огромным преимуществом.

Пример GDScript

Некоторые люди могут научиться лучше, взглянув на синтаксис, поэтому вот простой пример того, как выглядит GDScript.

# A file is a class!

# Inheritance

extends BaseClass

# (optional) class definition with a custom icon

class_name MyClass, "res://path/to/optional/icon.svg"

# Member variables

var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key": "value", 2: 3}
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)

# Function

func some_function(param1, param2):
    var local_var = 5

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

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

    while param2 != 0:
        param2 -= 1

    var local_var2 = param1 + 3
    return local_var2

# Functions override functions with the same name on the base/parent class.
# If you still want to call them, use '.' (like 'super' in other languages).

func something(p1, p2):
    .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#, но вы никогда раньше не использовали динамически типизированный язык, рекомендуется прочитать это руководство: doc_gdscript_efficient.

Язык

Далее следует общий обзор GDScript. Подробную информацию, например, о доступных методах для массивов или других объектов, следует искать в описаниях связанных классов.

Идентификаторы

Любая строка, которая ограничивает себя алфавитными символами (от a до z и от A до Z), цифрами (от 0 до 9) и _ квалифицируется как идентификатор. Кроме того, идентификаторы не должны начинаться с цифры. Идентификаторы чувствительны к регистру (foo отличается от FOO).

Ключевые слова

Ниже приведен список ключевых слов, поддерживаемых языком. Поскольку ключевые слова являются зарезервированными словами (токенами), они не могут использоваться в качестве идентификаторов. Операторы (например, in, not, and или or) и имена встроенных типов, перечисленные в следующих разделах, также зарезервированы.

Ключевые слова определены в токенизаторе GDScript, если вы хотите взглянуть под капот.

Ключевое слово Описание
if Смотрите if/else/elif.
elif Смотрите if/else/elif.
else Смотрите if/else/elif.
for См. for.
while Смотрите while.
match Смотрите match.
break Выход из выполнения текущего for или while циклов.
continue Немедленный переход к следующей итерации for или while циклов.
pass Используется там, где наличие инструкции требуется синтаксически, но выполнение ее кода нежелательно. Например, в пустых функциях.
return Возвращает значение из функции.
class Объявляет класс.
extends Объявляет какой класс расширяет текущий класс.
is Tests whether a variable extends a given class, or is of a given built-in type.
as Cast the value to a given type if possible.
self Ссылается на текущий экземпляр класса.
tool Выполняет скрипт в редакторе.
signal Объявляет сигнал.
func Объявляет функцию.
static Объявление статической функции. Статические поля класса не доступны.
const Объявляет константу.
enum Объявляет перечисление.
var Объявляет переменную.
onready Инициализирует переменную, как только Узел, к которому прикреплен скрипт, а также его дети являются частью дерева сцен.
export Сохраняет переменную вместе с ресурсом, к которому она привязана, и делает ее видимой и модифицируемой в редакторе.
setget Определяет функции setter и getter для переменной.
breakpoint Помощник редактора для контрольных точек отладчика.
preload Предварительно загружает класс или переменную. См. Классы как ресурсы.
yield Поддержка сопрограмм. См. Сопрограммы с yield.
assert Задает условие, регистрирует ошибку при сбое. Игнорируется в не отладочных сборках. См. Ключевое слово Assert.
remote Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня.
master Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня.
puppet Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня.
remotesync Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня.
mastersync Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня.
puppetsync Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня.
PI Константа Пи.
TAU Константа Тау.
INF Бесконечность. Используется для сравнений.
NAN NAN (англ. Not-a-Number, «не число»). Используется для сравнений.

Операторы

Далее приведен список поддерживаемых операторов и их приоритет.

Оператор Описание
x[index] Subscription (highest priority)
x.attribute Attribute reference
is Instance type checker
~ Побитовое НЕ
-x Negative / Unary negation
* / %

Умножение / Деление / Остаток от деления

Эти операторы ведут себя так же, как и в C++. Целочисленное деление усекается, а не возвращает дробное число, и оператор % доступен только для значений типа int(«fmod» для типа float)

+ Addition / Concatenation of arrays
- Вычитание
<< >> Bit shifting
& Побитовое И
^ Побитовое Исключающее ИЛИ
| Побитовое ИЛИ
< > == != >= <= Сравнения
in Content test
! not Логическое НЕТ
and && Логическое И
or || Логическое ИЛИ
if x else Тернарный оператор Если/Иначе (if/else)
= += -= *= /= %= &= |= Assignment (lowest priority)

Литералы

Литерал Тип
45 Целое число в десятичной системе счисления
0x8F51 Base 16 (hexadecimal) integer
0b101010 Base 2 (binary) integer
3.14, 58.1e-10 Floating-point number (real)
"Привет", "Дарова!" Строки
"""Приииивет""" Многострочная строка
@"Node/Label" class_NodePath or StringName
$NodePath Сокращение для get_node("NodePath")

Комментарии

Все, что находится после символа # до конца строки игнорируется и считается комментарием.

# This is a comment.

Встроенные типы

Built-in types are stack-allocated. They are passed as values. This means a copy is created on each assignment or when passing them as arguments to functions. The only exceptions are Arrays and Dictionaries, which are passed by reference so they are shared. (Pooled arrays such as PoolByteArray are still passed as values.)

Базовые встроенные типы

Переменная в GDScript может быть определена несколькими встроенными типами.

null

null - это пустой тип данных, который не содержит никакой информации и не может принимать другие значения.

bool

Short for «boolean», it can only contain true or false.

int

Short for «integer», it stores whole numbers (positive and negative). It is stored as a 64-bit value, equivalent to «int64_t» in C++.

float

Stores real numbers, including decimals, using floating-point values. It is stored as a 64-bit value, equivalent to «double» in C++. Note: Currently, data structures such as Vector2, Vector3, and PoolRealArray store 32-bit single-precision «float» values.

String

A sequence of characters in Unicode format. Strings can contain standard C escape sequences. GDScript also supports Строки формата GDScript.

Векторные встроенные типы

Vector2

2D вектор, содержащий поля x и y. Также может быть доступен как массив.

Rect2

2D Rectangle type containing two vectors fields: position and size. Also contains an end field which is position + size.

Vector3

3D векторный тип, содержащий поля x, y и z. Может быть получен как массив.

Transform2D

3×2 matrix used for 2D transforms.

Plane

3D Plane type in normalized form that contains a normal vector field and a d scalar distance.

Quat

Quaternion - это тип данных, используемый для представления трехмерного вращения. Полезно для интерполяции вращений.

AABB

Axis-aligned bounding box (or 3D box) contains 2 vectors fields: position and size. Also contains an end field which is position + size.

Basis

Матрица 3x3, используемая для трехмерного вращения и масштабирования. Он содержит 3 векторных поля (x, y и z), а также доступен в виде массива трехмерных векторов.

Transform

3D-преобразование содержит поле Basis basis и поле Vector3 origin.

Встроенные типы движка

Color

Color - тип данных цвета, содержит поля r, g, b, и a. Он также доступен как h, s, и v для оттенка(hue)/насыщенности(saturation)/значения(value).

NodePath

Составной путь к узлу, используемый в основном в системе сцен. Он может быть легко переведен на строку и из нее.

RID

Resource ID (RID). Servers use generic RIDs to reference opaque data.

Object

Базовый класс для всего, что не является встроенным типом.

Встроенные типы контейнеров

Array

Generic sequence of arbitrary object types, including other arrays or dictionaries (see below). The array can resize dynamically. Arrays are indexed starting from index 0. Negative indices count from the end.

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].

GDScript arrays are allocated linearly in memory for speed. Large arrays (more than tens of thousands of elements) may however cause memory fragmentation. If this is a concern, special types of arrays are available. These only accept a single data type. They avoid memory fragmentation and use less memory, but are atomic and tend to run slower than generic arrays. They are therefore only recommended to use for large data sets:

  • PoolByteArray: Массив байтов (целые числа от 0 до 255).
  • PoolIntArray: Массив целых чисел.
  • PoolRealArray: Массив чисел с плавающей точкой.
  • PoolStringArray: Массив строк.
  • PoolVector2Array: Массив объектов типа Vector2.
  • PoolVector3Array: Массив объектов типа Vector3.
  • PoolColorArray: Массив объектов типа Color.

Dictionary

Ассоциативный контейнер, содержащий значения, на которые ссылаются уникальные ключи.

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. Lua-стиль использует = вместо : и не использует кавычки для разметки ключей строк (что делает написание немного быстрее). Обратите внимание, что, как и любой другой идентификатор GDScript, ключи, написанные в этом виде, не могут начинаться с цифры.

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

Чтобы добавить ключ к существующему словарю, воспользуйтесь им как существующим и назначьте ему:

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 и при желании, есть возможность присвоить значение при инициализации.

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 = Sprite.new()

Если переменная инициализирована в объявлении, то можно предугадать типе сразу, поэтому можно не писать имя типа:

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

Предсказание типа возможно только в том случае, если заданное значение имеет определенный тип, в противном случае возникает ошибка.

Поддерживаемые типы:

  • Built-in types (Array, Vector2, int, String, etc.).
  • Engine classes (Node, Resource, Reference, etc.).
  • Константы, если они содержат скрипт ресурса (MyScript если вы объявили const MyScript = preload("res://my_script.gd")).
  • Other classes in the same script, respecting scope (InnerClass.NestedClass if you declared class NestedClass inside the class InnerClass in the same scope).
  • Классы объявленные в скрипте с ключевым словом class_name.

Приведение переменных

Значения, присваиваемые типизированным переменным, должны иметь совместимый тип. Если необходимо заставить значение быть определенного типа, в частности, для типов объектов, можно использовать оператор приведения as.

Casting between object types results in the same object if the value is of the same type or a subtype of the cast type.

var my_node2D: Node2D
my_node2D = $Sprite as Node2D # Works since Sprite 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

Casting is also useful to have better type-safe variables when interacting with the scene tree:

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

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

Константы

Константы аналогичны переменным, но должны быть постоянными или постоянными выражениями и присваиваются при инициализации.

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.

Хотя тип констант берется из присвоенного значения, можно также явно описать тип:

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

Присвоение значения несовместимого типа приведет к ошибке.

Перечисления

Перечисления это фактически сокращения для констант, и они очень полезны если вы хотите присвоить конкретные числовые значения некоторым константам.

If you pass a name to the enum, it will put all the keys inside a constant dictionary of that name.

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 State {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT}
# Is the same as:
const State = {STATE_IDLE = 0, STATE_JUMP = 5, STATE_SHOOT = 6}
# Access values with State.STATE_IDLE, etc.

Функции

Функции всегда принадлежат классу. Приоритет области видимости для переменных: локальный → член класса → глобальный. Переменная self всегда доступна и предоставляется в опционально для доступа к элементам класса, но не всегда требуется (и в отличие от Python, она не должна передаваться в качестве первого аргумента функции).

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

Функция может return (возвращать) значения. Если вы ничего не возвращаете, то она является null.

Функции могут также иметь спецификацию типов для аргументов и возвращаемого значения. Типы аргументам можно добавлять аналогично переменным:

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 означает, что функция ничего не возвращает. Пустые функции могут возвращаться раньше с ключевым словом return, но они не могут возвращать значения.

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

Примечание

Не пустые (Non-void) функции должны всегда возвращать значение, поэтому если в вашем коде есть ветвистые выражения (такие как конструкция if/else), все возможные пути должны иметь ответвление. Например, если return внутри блока if, но не после него, то редактор выдаст ошибку, потому что если условие не будет выполнено, то функция не будет иметь действительного значения для возврата.

Referencing functions

Contrary to Python, functions are not first-class objects in GDScript. This means they cannot be stored in variables, passed as an argument to another function or be returned from other functions. This is for performance reasons.

To reference a function by name at run-time, (e.g. to store it in a variable, or pass it to another function as an argument) one must use the call or funcref helpers:

# Call a function by name in one step.
my_node.call("my_function", args)

# Store a function reference.
var my_func = funcref(my_node, "my_function")
# Call stored function reference.
my_func.call_func(args)

Статические функции

A function can be declared static. When a function is static, it has no access to the instance member variables or self. This is mainly useful to make libraries of helper functions:

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

Операторы и контроль потока

Операторы являются стандартными и могут присваивать, вызовы функций, структуры управления потоком и т.д. (см. ниже). Разделитель ; является абсолютно необязательным.

if/else/elif

Простые условия создаются с помощью такого синтаксиса if/else/else/elif. Скобки вокруг условий допускаются, но не требуются. Учитывая характер отступа на основе табуляции, можно использовать 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

Sometimes, you might want to assign a different initial value based on a boolean expression. In this case, ternary-if expressions come in handy:

var x = [value] if [expression] else [value]
y += 3 if y < 10 else -1

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 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))

match

Оператор match используется для ветвления. Это эквивалентно оператору switch, встречающемуся на многих других языках, но match предлагает некоторые дополнительные возможности.

Basic syntax:

match [expression]:
    [pattern](s):
        [block]
    [pattern](s):
        [block]
    [pattern](s):
        [block]

Ускоренный курс для людей, знакомых с правилами switch:

  1. Replace switch with match.
  2. Remove case.
  3. Remove any breaks. If you don’t want to break by default, you can use continue for a fallthrough.
  4. Смените default на единичное подчеркивание.

Управление потоком:

Шаблоны проверяются сверху вниз. Если шаблон совпадает с значением, то будет выполнен соответствующий блок. После этого исполнение продолжится ниже оператора match (то есть другие варианты пропускаются). Если вы хотите продолжить проверку, вы можете использовать continue для остановки исполнения в текущем блоке и проверки тех, что ниже него.

Существует 6 типов шаблонов:

  • Constant pattern

    Constant primitives, like numbers and strings:

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

    Matches the contents of a variable/enum:

    match typeof(x):
        TYPE_REAL:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Wildcard pattern

    This pattern matches everything. It’s written as a single underscore.

    It can be used as the equivalent of the default in a switch statement in other languages:

    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.")
    
  • Binding pattern

    A binding pattern introduces a new variable. Like the wildcard pattern, it matches everything - and also gives that value a name. It’s especially useful in array and dictionary patterns:

    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)
    
  • Array pattern

    Matches an array. Every single element of the array pattern is a pattern itself, so you can nest them.

    Сначала проверяется длина массива, она должна быть того же размера, что и шаблон, в противном случае шаблон не совпадет.

    Open-ended array: An array can be bigger than the pattern by making the last subpattern ...

    Every subpattern has to be comma-separated.

    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")
    
  • Dictionary pattern

    Работает так же, как и шаблон массива. Каждый ключ должен быть постоянным шаблоном (constant pattern).

    Сначала проверяется размер словаря, он должен быть того же размера, что и шаблон, в противном случае шаблон не совпадет.

    Open-ended dictionary: A dictionary can be bigger than the pattern by making the last subpattern ...

    Каждый подшаблон должен быть разделен запятыми.

    Если вы не указываете значение, то проверяется только наличие ключа.

    A value pattern is separated from the key pattern with a :.

    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")
    
  • Multiple patterns

    You can also specify multiple patterns separated by a comma. These patterns aren’t allowed to have any bindings in them.

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

Классы

By default, all script files are unnamed classes. In this case, you can only reference them using the file’s path, using either a relative or an absolute path. For example, if you name a script file 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()

Instead, you can give your class a name to register it as a new type in Godot’s editor. For that, you use the class_name keyword. You can add an optional comma followed by a path to an image, to use it as an icon. Your class will then appear with its new icon in the editor:

# Item.gd

extends Node

class_name Item, "res://interface/icons/item.png"
../../../_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)

Примечание

Godot’s class syntax is compact: it can only contain member variables or functions. You can use static functions, but not static member variables. In the same way, the engine initializes variables every time you create an instance, and this includes arrays and dictionaries. This is in the spirit of thread safety, since scripts can be initialized in separate threads without the user knowing.

Наследование

A class (stored as a file) can inherit from:

  • A global class.
  • Another class file.
  • Внутреннего класса внутри другого файла класса.

Множественное наследование невозможно.

Inheritance uses the extends keyword:

# 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

To check if a given instance inherits from a given class, the is keyword can be used:

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

# [...]

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

To call a function in a parent class (i.e. one extend-ed in your current class), prepend . to the function name:

.base_func(args)

This is especially useful because functions in extending classes replace functions with the same name in their parent classes. If you still want to call them, you can prefix them with . (like the super keyword in other languages):

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

Примечание

Default functions like _init, and most notifications such as _enter_tree, _exit_tree, _process, _physics_process, etc. are called in all parent classes automatically. There is no need to call them explicitly when overloading them.

Конструктор класса

The class constructor, called on class instantiation, is named _init. As mentioned earlier, the constructors of parent classes are called automatically when inheriting a class. So, there is usually no need to call ._init() explicitly.

Unlike the call of a regular function, like in the above example with .some_func, if the constructor from the inherited class takes arguments, they are passed like this:

func _init(args).(parent_args):
   pass

This is better explained through examples. Consider this scenario:

# 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).(e):
    # Do something with 'e'.
    message = m

Здесь есть несколько вещей, который необходимо держать в голове:

  1. If the inherited class (State.gd) defines a _init constructor that takes arguments (e in this case), then the inheriting class (Idle.gd) must define _init as well and pass appropriate parameters to _init from State.gd.

  2. Idle.gd can have a different number of arguments than the parent class State.gd.

  3. In the example above, e passed to the State.gd constructor is the same e passed in to Idle.gd.

  4. If Idle.gd’s _init constructor takes 0 arguments, it still needs to pass some value to the State.gd parent class, even if it does nothing. This brings us to the fact that you can pass literals in the base constructor as well, not just variables. eg.:

    # Idle.gd
    
    func _init().(5):
        pass
    

Внутренние классы

Файл класса может хранить внутренние классы. Внутренние классы определяются с помощью ключевого слова class. Создать экземпляр можно с помощью функции ClassName.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()

Classes as resources

Классы, хранящиеся в файлах, рассматриваются как ресурсы. Они должны быть загружены с диска для доступа к ним в других классах. Это делается с помощью функций load или preload (см. ниже). Создание экземпляра загруженного ресурса класса осуществляется вызовом функции new на объекте класса:

# Load the class resource when calling load().
var my_class = 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()

Экспорт

Примечание

Documentation about exports has been moved to GDScript exports.

Сеттеры/геттеры

Часто полезно знать, когда переменная, входящая в состав класса, изменяется по какой-то причине. Возможно вы хотите ее инкапсулировать.

Для этого GDScript предоставляет синтаксис setter/getter, используя ключевое слово setget. Использовать непосредственно после определения переменной:

var variable = value setget setterfunc, getterfunc

Whenever the value of variable is modified by an external source (i.e. not from local usage in the class), the setter function (setterfunc above) will be called. This happens before the value is changed. The setter must decide what to do with the new value. Vice versa, when variable is accessed, the getter function (getterfunc above) must return the desired value. Below is an example:

var my_var setget my_var_set, my_var_get

func my_var_set(new_value):
    my_var = new_value

func my_var_get():
    return my_var # Getter must return a value.

Either of the setter or getter functions can be omitted:

# Only a setter.
var my_var = 5 setget my_var_set
# Only a getter (note the comma).
var my_var = 5 setget ,my_var_get

Setters and getters are useful when exporting variables to the editor in tool scripts or plugins, for validating input.

Как уже говорилось, локальный доступ не будет запускать сеттер и геттер. Вот иллюстрация этого:

func _init():
    # Does not trigger setter/getter.
    my_integer = 5
    print(my_integer)

    # Does trigger setter/getter.
    self.my_integer = 5
    print(self.my_integer)

Режим Инструмента

By default, scripts don’t run inside the editor and only the exported properties can be changed. In some cases, it is desired that they do run inside the editor (as long as they don’t execute game code or manually avoid doing so). For this, the tool keyword exists and must be placed at the top of the file:

tool
extends Button

func _ready():
    print("Hello")

Предупреждение

Be cautious when freeing nodes with queue_free() or free() in a tool script (especially the script’s owner itself). As tool scripts run their code in the editor, misusing them may lead to crashing the editor.

Управление памятью

If a class inherits from class_Reference, then instances will be freed when no longer in use. No garbage collector exists, just reference counting. By default, all classes that don’t define inheritance extend Reference. If this is not desired, then a class must inherit class_Object manually and must call instance.free(). To avoid reference cycles that can’t be freed, a weakref function is provided for creating weak references.

Alternatively, when not using references, the is_instance_valid(instance) can be used to check if an object has been freed.

Сигналы

Signals are a tool to emit messages from an object that other objects can react to. To create custom signals for a class, use the signal keyword.

extends Node

# A signal named health_depleted
signal health_depleted

Примечание

Signals are a Callback mechanism. They also fill the role of Observers, a common programming pattern. For more information, read the Observer tutorial in the Game Programming Patterns ebook.

You can connect these signals to methods the same way you connect built-in signals of nodes like class_Button or class_RigidBody.

In the example below, we connect the health_depleted signal from a Character node to a Game node. When the Character node emits the signal, the game node’s _on_Character_health_depleted is called:

# Game.gd

func _ready():
   var character_node = get_node('Character')
   character_node.connect("health_depleted", self, "_on_Character_health_depleted")

func _on_Character_health_depleted():
   get_tree().reload_current_scene()

You can emit as many arguments as you want along with a signal.

Here is an example where this is useful. Let’s say we want a life bar on screen to react to health changes with an animation, but we want to keep the user interface separate from the player in our scene tree.

In our Character.gd script, we define a health_changed signal and emit it with Object.emit_signal(), and from a Game node higher up our scene tree, we connect it to the Lifebar using the Object.connect() method:

# 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
    emit_signal("health_changed", 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)
...

Примечание

To use signals, your class has to extend the Object class or any type extending it like Node, KinematicBody, Control

In the Game node, we get both the Character and Lifebar nodes, then connect the character, that emits the signal, to the receiver, the Lifebar node in this case.

# Game.gd

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

   character_node.connect("health_changed", lifebar_node, "_on_Character_health_changed")

This allows the Lifebar to react to health changes without coupling it to the Character node.

You can write optional argument names in parentheses after the signal’s definition:

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

These arguments show up in the editor’s node dock, and Godot can use them to generate callback functions for you. However, you can still emit any number of arguments when you emit signals; it’s up to you to emit the correct values.

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

GDScript can bind an array of values to connections between a signal and a method. When the signal is emitted, the callback method receives the bound values. These bound arguments are unique to each connection, and the values will stay the same.

You can use this array of values to add extra constant information to the connection if the emitted signal itself doesn’t give you access to all the data that you need.

Building on the example above, let’s say we want to display a log of the damage taken by each character on the screen, like Player1 took 22 damage.. The health_changed signal doesn’t give us the name of the character that took damage. So when we connect the signal to the in-game console, we can add the character’s name in the binds array argument:

# Game.gd

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

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

Our BattleLog node receives each element in the binds array as an extra argument:

# BattleLog.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."

Coroutines with yield

GDScript offers support for coroutines via the yield built-in function. Calling yield() will immediately return from the current function, with the current frozen state of the same function as the return value. Calling resume() on this resulting object will continue execution and return whatever the function returns. Once resumed, the state object becomes invalid. Here is an example:

func my_func():
   print("Hello")
   yield()
   print("world")

func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print("my dear")
    y.resume()
    # 'y' resumed and is now an invalid state.

Will print:

Hello
my dear
world

It is also possible to pass values between yield() and resume(), for example:

func my_func():
   print("Hello")
   print(yield())
   return "cheers!"

func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print(y.resume("world"))
    # 'y' resumed and is now an invalid state.

Will print:

Hello
world
cheers!

Корутины и сигналы

The real strength of using yield is when combined with signals. yield can accept two arguments, an object and a signal. When the signal is received, execution will recommence. Here are some examples:

# Resume execution the next frame.
yield(get_tree(), "idle_frame")

# Resume execution when animation is done playing.
yield(get_node("AnimationPlayer"), "finished")

# Wait 5 seconds, then resume execution.
yield(get_tree().create_timer(5.0), "timeout")

Coroutines themselves use the completed signal when they transition into an invalid state, for example:

func my_func():
    yield(button_func(), "completed")
    print("All buttons were pressed, hurray!")

func button_func():
    yield($Button0, "pressed")
    yield($Button1, "pressed")

my_func будет продолжаться только после нажатия обеих кнопок.

Ключевое слово Onready

When using nodes, it’s common to desire to keep references to parts of the scene in a variable. As scenes are only warranted to be configured when entering the active scene tree, the sub-nodes can only be obtained when a call to Node._ready() is made.

var my_label

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

This can get a little cumbersome, especially when nodes and external references pile up. For this, GDScript has the onready keyword, that defers initialization of a member variable until _ready() is called. It can replace the above code with a single line:

onready var my_label = get_node("MyLabel")

Ключевое слово Assert

The assert keyword can be used to check conditions in debug builds. These assertions are ignored in non-debug builds. This means that the expression passed as argument won’t be evaluated in a project exported in release mode. Due to this, assertions must not contain expressions that have side effects. Otherwise, the behavior of the script would vary depending on whether the project is run in a debug build.

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

When running a project from the editor, the project will be paused if an assertion error occurs.