Основы GDScript¶
Введение¶
GDScript это высокоуровневый, динамически типизированный язык программирования. Он использует синтаксис похожий на Python (блоки выделяются табуляцией и множество ключевых слов идентичны). Его цель быть оптимизированным и плотно интегрированным в движок Godot, позволяя достичь высокой гибкости в создании контента и интеграции.
История¶
Примечание
Документация по истории GDScript перемещена в Часто задаваемые вопросы.
Пример 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 |
Выход из выполнения текущего |
continue |
Немедленный переход к следующей итерации |
pass |
Используется там, где наличие инструкции требуется синтаксически, но выполнение ее кода нежелательно. Например, в пустых функциях. |
return |
Возвращает значение из функции. |
class |
Определяет внутренний класс. |
class_name |
Привязка названия класса и необязательной иконки для вашего скрипта. |
extends |
Объявляет какой класс расширяет текущий класс. |
is |
Проверяет, расширяет ли переменная данный класс, или принадлежит ли она данному встроенному типу. |
as |
Приводит значение к данному типу, если это возможно. |
self |
Ссылается на текущий экземпляр класса. |
tool |
Выполняет скрипт в редакторе. |
signal |
Объявляет сигнал. |
func |
Объявляет функцию. |
static |
Объявление статической функции. Статические поля класса не доступны. |
const |
Объявляет константу. |
enum |
Объявляет перечисление. |
var |
Объявляет переменную. |
onready |
Инициализирует переменную, как только Узел, к которому прикреплен скрипт, а также его дети являются частью дерева сцен. |
export |
Сохраняет переменную вместе с ресурсом, к которому она привязана, и делает ее видимой и модифицируемой в редакторе. |
setget |
Определяет функции setter и getter для переменной. |
breakpoint |
Помощник редактора для контрольных точек отладчика. |
preload |
Предварительно загружает класс или переменную. См. Классы как ресурсы. |
yield |
Поддержка сопрограмм. См. Сопрограммы с промежуточным возвратом. |
assert |
Задает условие, регистрирует ошибку при сбое. Игнорируется в не отладочных сборках. См. Ключевое слово Assert. |
remote |
Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня. |
master |
Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня. |
puppet |
Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня. |
remotesync |
Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня. |
mastersync |
Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня. |
puppetsync |
Сетевая аннотация RPC. См. документацию по многопользовательскому режиму высокого уровня. |
PI |
Константа Пи. |
TAU |
Константа Тау. |
INF |
Бесконечность. Используется для сравнений. |
NAN |
NAN (англ. Not-a-Number, "не число"). Используется для сравнений. |
Операторы¶
Далее приведен список поддерживаемых операторов и их приоритет.
Оператор |
Описание |
|
Подписка (высший приоритет) |
|
Ссылка на атрибут |
|
Вызов функции |
|
Проверка типа экземпляра |
|
Побитовое НЕ |
|
Отрицательное / унарное отрицание |
|
Умножение / Деление / Остаток от деления Эти операторы ведут себя так же, как C++. Целочисленное деление усекается, а не возвращает дробное число, а оператор % доступен только для целых чисел ("fmod" для чисел с плавающей запятой) и дополнительно используется для Format Strings |
|
Добавление / объединение массивов |
|
Вычитание |
|
Битовый сдвиг |
|
Побитовое И |
|
Побитовое Исключающее ИЛИ |
|
Побитовое ИЛИ |
|
Сравнения |
|
When used with the |
|
Логическое НЕ |
|
Логическое И |
|
Логическое ИЛИ |
|
Тернарный оператор Если/Иначе (if/else) |
|
Приведение типов |
|
Присваивание (низший приоритет) |
Литералы¶
Литерал |
Тип |
|
Целое число в десятичной системе счисления |
|
Основание 16 (шестнадцатеричное) целое число |
|
Целое число в двоичной системе счисления |
|
Число с плавающей точкой (вещественное число) |
|
Строки |
|
Многострочная строка |
|
NodePath или StringName |
|
Сокращение для |
Типы данных int и float могут объявляться с номерами, разделёнными _
, для удобочитаемого вида. Допустимы следующие способы записи чисел:
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.
Встроенные типы¶
Встроенные типы распределены по стекам. Они передаются как значения. Это означает, что копия создается на каждом присваивании или при передаче их в качестве аргументов функциям. Единственным исключением являются Array
и Dictionaries
, которые передаются по ссылке и являются общими. (Не PoolArray
типа PoolByteArray
, но они тоже передаются как значения, поэтому учитывайте это при решении, что именно нужно использовать для Вашего проекта!)
Базовые встроенные типы¶
Переменная в GDScript может быть определена несколькими встроенными типами.
null¶
null
- это пустой тип данных, который не содержит никакой информации и не может принимать другие значения.
bool¶
Сокращенно от "boolean", оно может содержать только true
или false
.
int¶
Сокращённо от "integer", содержит целые числа (положительные и отрицательные). Хранится как 64-битное значение, эквивалент "int64_t" в C++.
float¶
Содержит действительные числа, включая десятичные, используя значения с плавающей запятой. Хранится как 64-битное значение, эквивалент "double" в C++. Примечание: В настоящее время структуры данных, такие как Vector2, Vector3 и PoolRealArray хранятся как 32-битные плавающие одинарные значения "float".
String¶
Последовательность символов в формате Unicode <https://en.wikipedia.org/wiki/Unicode> `_. Строки могут содержать следующие escape-последовательности:
Escape-последовательность |
Означает |
|
Новая строка (перевод строки) |
|
Символ горизонтальной табуляции |
|
Возврат каретки |
|
Оповещение (звуковой сигнал/звонок) |
|
Backspace |
|
Разрыв страницы |
|
Символ вертикальной табуляции |
|
Двойная кавычка |
|
Одиночная кавычка |
|
Обратная косая черта |
|
Символ Unicode ``XXXX``(шестнадцатеричный, без учета регистра ) |
GDScript также поддерживает Строки формата GDScript.
Векторные встроенные типы¶
Vector2¶
2D-векторный тип, содержащий поля x
и y
. Также может быть доступен как массив.
Rect2¶
2D Прямоугольник, содержащий два поля векторов: position
и size
. Альтернативно содержит поле end
, которое представляет собой position + size
.
Vector3¶
3D векторный тип, содержащий поля x
, y
и z
. Может быть получен как массив.
Transform2D¶
Матрица 3x2, используемая для 2D-преобразований.
Plane¶
Тип трехмерной плоскости в нормализованной форме, которая содержит normal
векторное поле и d
скалярное расстояние.
Quat¶
Quaternion - это тип данных, используемый для представления трехмерного вращения. Полезно для интерполяции вращений.
AABB¶
Выровненная по оси ограничительная рамка (или трехмерная коробка) содержит 2 поля типа Vector: position
и size
. Также содержит поле end
, которое является 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). Серверы используют общие RID для создания ссылок на непрозрачные объекты.
Object¶
Базовый класс для всего, что не является встроенным типом.
Встроенные типы контейнеров¶
Array¶
Общая последовательность любых типов объектов, включая другие массивы или словари (см. ниже). Массив может изменяться динамически. Массивы индексируются, начиная с индекса 0
. Начиная с Godot 2.1, индексы могут быть отрицательными, как и в Python, считая с конца.
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 выделяются в памяти линейно для повышения скорости работы. Однако большие массивы (более десятков тысяч элементов) могут привести к фрагментации памяти. Если это проблема, то доступны специальные типы массивов. Они принимают только один тип данных. Они избегают фрагментации памяти и также потребляют меньше памяти, но являются атомными и, как правило, работают медленнее, чем обычные массивы. Поэтому их рекомендуется использовать только для больших наборов данных:
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 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])
Примечание
Синтаксис скобок может быть использован для доступа к свойствам любого объекта Object, а не только для словарей. мейте в виду, что это вызовет ошибку скрипта при попытке индексировать несуществующее свойство. Чтобы этого избежать, вместо этого используйте методы Object.get() и Object.set().
Данные¶
Переменные¶
Переменные могут существовать как члены класса или быть локальными для функций. Они создаются с помощью ключевого слова 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'.
Предсказание типа возможно только в том случае, если заданное значение имеет определенный тип, в противном случае возникает ошибка.
Поддерживаемые типы:
Встроенные типы (Array, Vector2, int, String и т.д.).
Классы движка (Node, Resource, Reference, etc.).
Константы, если они содержат скрипт ресурса (
MyScript
если вы объявилиconst MyScript = preload("res://my_script.gd")
).Другие классы в том же скрипте, учитывающие область видимости (
InnerClass.NestedClass
если вы объявилисlass NestedClass
внутрисlass InnerClass
в той же области видимости).Классы объявленные в скрипте с ключевым словом
class_name
.
Приведение переменных¶
Значения, присваиваемые типизированным переменным, должны иметь совместимый тип. Если необходимо заставить значение быть определенного типа, в частности, для типов объектов, можно использовать оператор приведения as
.
Приведение типов объектов приводит к одному и тому же объекту, если значение имеет один и тот же тип или подтип приведенного типа.
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.
Приведение также полезно для получения более надежных типов переменных при взаимодействии с деревом:
# 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
, вам разрешается присвоить константное значение переменной. Повтор этого трюка с уже объявленной константой — вызовет ошибку.
Мы рекомендуем использовать константы там, где подразумевается неизменность их значений.
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()
Присвоение значения несовместимого типа приведет к ошибке.
Примечание
Поскольку массивы и словари передаются по ссылке, константы являются "плоскими". Это означает, что если вы объявите постоянный массив или словарь, он все равно может быть изменен впоследствии. Однако они не могут быть переназначены с другим значением.
Перечисления¶
Перечисления это фактически сокращения для констант, и они очень полезны если вы хотите присвоить конкретные числовые значения некоторым константам.
Если вы передадите имя перечислению, оно поместит все значения в постоянный словарь (constant dictionary) с этим именем.
Важно
В Godot версии 3.1 и выше ключи именованного перечисления не регистрируются как глобальные константы. Для доступа к ним необходим префикс в виде названия перечисления (Name.KEY
); см. пример ниже.
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
, но они не могут возвращать значения.
func void_function() -> void:
return # Can't return a value
Примечание
Не пустые (Non-void) функции должны всегда возвращать значение, поэтому если в вашем коде есть ветвистые выражения (такие как конструкция if
/else
), все возможные пути должны иметь ответвление. Например, если return
внутри блока if
, но не после него, то редактор выдаст ошибку, потому что если условие не будет выполнено, то функция не будет иметь действительного значения для возврата.
Ссылочные функции¶
В отличие от Python, функции не являются объектами первого класса в GDScript. Это означает, что они не могут храниться в переменных, передаваться в качестве аргумента другой функции или возвращаться из других функций. Это все ради производительности.
Для обращения к функции по имени во время выполнения (например, для хранения ее в переменной или передачи другой функции в качестве аргумента) необходимо использовать помощники call
или funcref
:
# 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)
Статические функции¶
Функция может быть объявлена как статическая. Когда функция статична, она не имеет доступа к переменным, входящим в состав экземпляра, или к self
. Это, в основном, полезно для создания библиотек вспомогательных функций:
static func sum2(a, b):
return a + b
Операторы и контроль потока¶
Операторы являются стандартными и могут быть присваиваниями, вызовами функций, структурами управления потоком и т.д. (см. ниже). Разделитель ;
является абсолютно необязательным.
if/else/elif¶
Простые условия создаются с помощью такого синтаксиса if
/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
Иногда на основе логического выражения может потребоваться присвоить другое начальное значение. В этом случае могут пригодиться выражения тернарного if:
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
You may also wish to check if a value is contained within something. You can
use an if
statement combined with the in
operator to accomplish this:
# 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 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
предлагает некоторые дополнительные возможности.
Основной синтаксис:
match [expression]:
[pattern](s):
[block]
[pattern](s):
[block]
[pattern](s):
[block]
Ускоренный курс для людей, знакомых с правилами switch:
Замените
switch
наmatch
.Уберите
case
.Удалите все
break
. Если вы не хотите чтобыbreak
был по умолчанию, вы можете использоватьcontinue
для продолжения проверки.Смените
default
на единичное подчеркивание.
Управление потоком:
Шаблоны проверяются сверху вниз. Если шаблон совпадает со значением, то будет выполнен соответствующий блок. После этого исполнение продолжится ниже оператора match
(то есть другие варианты пропускаются). Если вы хотите продолжить проверку, вы можете использовать continue
для остановки исполнения в текущем блоке и проверки тех, что ниже него.
Существует 6 типов шаблонов:
- Постоянный шаблон
Постоянные (константные) примитивы, такие как числа и строки
match x: 1: print("We are number one!") 2: print("Two are better than one!") "test": print("Oh snap! It's a string!")
- Шаблон переменной
Соответствует содержимому переменной/перечисления(enum):
match typeof(x): TYPE_REAL: print("float") TYPE_STRING: print("text") TYPE_ARRAY: print("array")
- Шаблон подстановки
Этот шаблон подходит для любого поиска. Это пишется как единичное подчёркивание.
Он может быть использован как эквивалент выражения
default
в выраженииswitch
на других языках.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")
Классы¶
По умолчанию, все файлы скриптов это классы без имен. В этом случае вы можете ссылаться на них только по пути файла, с помощью относительного или абсолютного пути. Например, если вы назовете файл скрипта 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
extends Node
class_name Item, "res://interface/icons/item.png"

Предупреждение
Если сценарий находится в каталоге res://addons/
, class_name
приведет к тому, что узел будет отображаться в диалоговом окне Создать новый узел, только если сценарий является частью включено плагина редактора. Смотрите: ref:doc_making_plugins для получения дополнительной информации.
Вот пример файла класса:
# 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 компактен: он может содержать только внутренние переменные или функции. Вы можете использовать статические функции, но не статические внутренние переменные. Таким же образом, движок инициализирует переменные каждый раз, когда вы создаете экземпляр, и это включает массивы и словари. Это ради безопасности потока, так как скрипты могут быть инициализированы в отдельных потоках без ведома пользователя.
Наследование¶
Класс (хранимый как файл) может наследоваться от:
Глобального класса.
Другого файла класса.
Внутреннего класса внутри другого файла класса.
Множественное наследование невозможно.
При наследовании использует ключевое слово 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
Чтобы проверить, наследуется ли данный экземпляр от данного класса, можно использовать ключевое слово is
:
# Cache the enemy class.
const Enemy = preload("enemy.gd")
# [...]
# Use 'is' to check inheritance.
if entity is Enemy:
entity.apply_damage()
Чтобы вызвать функцию, находящуюся в родительском классе (т.е. функцию extend
-класса в текущем классе), добавьте .
к имени функции:
.base_func(args)
Это особенно полезно, поскольку функции в классах-потомках заменяют (переопределяют) функции с тем же именем в своих родительских классах. Если вы хотите вызвать функцию из класса-родителя, вызывайте их, поставив .
перед именем функции (как и ключевое слово super в других языках):
func some_func(x):
.some_func(x) # Calls the same function on the parent class.
Примечание
Функции по умолчанию, такие как _init
, и большинство уведомлений, таких как _enter_tree
, _exit_tree
, _process
, _physics_process
и др. вызываются во всех родительских классах автоматически. Нет необходимости вызывать их вручную при перегрузке этих функций.
Конструктор класса¶
Конструктор класса, вызываемый при создании экземпляра класса, называется _init
. Как упоминалось ранее, конструкторы родительских классов вызываются автоматически при наследовании класса. Таким образом, обычно нет необходимости явно вызывать ._init ()
.
В отличие от вызова обычной функции, как в приведенном выше примере с .some_func
, если конструктор из унаследованного класса принимает аргументы, то они передаются следующим образом:
func _init(args).(parent_args):
pass
Это лучше объяснить на примере. Предположим, у нас следующая ситуация:
# 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
Здесь есть несколько вещей, которые необходимо держать в голове:
Если унаследованный класс (
State.gd
) определяет конструктор_init
, который принимает аргументы (в данном случаеe
), то наследованный класс (Idle.gd
) также должен определить_init
и передать соответствующие параметры в этот_init
изState.gd
.Количество аргументов в
Idle.gd
может отличаться от количества аргументов в базовом классеState.gd
.в приведенном выше примере,
e
, переданный конструкторуState.gd
является тем жеe
переданным вIdle.gd
.Если конструктор
_init
вIdle.gd
принимает ноль аргументов, то ему все равно нужно передать некоторое значение в родительский классState.gd
, даже если он ничего не делает. Что подводит нас к тому, что литералы можно передавать не только переменным, но и в базовом конструкторе. Например:# 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()
Классы как ресурсы¶
Классы, хранящиеся в файлах, рассматриваются как ресурсы. Они должны быть загружены с диска для доступа к ним в других классах. Это делается с помощью функций load
или preload
(см. ниже). Создание экземпляра загруженного ресурса класса осуществляется вызовом функции 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.
Сеттеры/геттеры¶
Часто полезно знать, когда переменная, входящая в состав класса, изменяется по какой-то причине. Возможно вы хотите ее инкапсулировать.
Для этого GDScript предоставляет синтаксис setter/getter, используя ключевое слово setget
. Использовать непосредственно после определения переменной:
var variable = value setget setterfunc, getterfunc
Всякий раз, когда значение variable
изменяется внешним источником (т.е. не от локального использования в классе), вызывается функция setter (setterfunc
выше). Это происходит до того, как значение изменится. Сеттер должен решить, что делать с новым значением. И наоборот, когда variable
доступна, функция getter (getterfunc
выше) должна return(возвращать)
желаемое значение. Ниже приведен пример:
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.
Любая из функций setter или getter может быть пропущена:
# 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
Сеттеры и геттеры полезны при экспорте переменных в редактор в инструментах скриптов или плагинов для проверки ввода.
Как уже говорилось, локальный доступ не будет запускать сеттер и геттер. Вот иллюстрация этого:
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)
Режим Инструмента¶
Скрипты по умолчанию не запускаются внутри редактора, и только экспортируемые свойства могут быть изменены. В некоторых случаях желательно, чтобы они выполнялись внутри редактора (до тех пор, пока они не выполняют код игры или не избегают этого вручную). Для этого существует ключевое слово tool
, которое должно размещаться в верхней части файла:
tool
extends Button
func _ready():
print("Hello")
Предупреждение
Будьте осторожны при освобождении узлов с помощью queue_free()
или free()
в скрипт инструмента (особенно самого владельца скрипта). Так как скрипт инструмента запускает свой код в редакторе, неправильное их использование может привести к крашу редактора.
Управление памятью¶
Если класс наследуется от Reference, то экземпляры будут удалены, когда они больше не будут использоваться. Вместо сборщика мусора происходит подсчет ссылок. По умолчанию, все классы, которые не определяют наследование, расширяются Reference. Если это нежелательно, то класс должен наследовать Object вручную и вызывать instance.free(). Чтобы избежать циклов ссылок, которые не могут быть освобождены, для создания слабых ссылок предусмотрена функция weakref
. Вот пример:
extends Node
var my_node_ref
func _ready():
my_node_ref = weakref(get_node("MyNode"))
func _this_is_called_later():
var my_node = my_node_ref.get_ref()
if my_node:
my_node.do_something()
Или же, если ссылки не используются, is_instance_valid(instance) может быть использован для проверки того, был ли освобожден объект.
Сигналы¶
Сигналы это способ отправки уведомлений от объекта, на которые могут реагировать другие объекты. Чтобы создать собственные сигналы для класса, используйте ключевое слово signal
.
extends Node
# A signal named health_depleted.
signal health_depleted
Примечание
Сигналы это Callback механизм. Они также выполняют роль наблюдателя, распространенный шаблон программирования. Для лучшего понимания информации, читайте про шаблон наблюдатель в словаре Шаблонов Игрового Программирования.
Вы можете подключать эти сигналы к методам точно так же, как вы подключаете встроенные сигналы узлов типа Button или RigidBody.
В примере ниже мы соединили сигнал health_depleted
от узла Character
с узлом Game
. Когда узел Character
отправляет сигнал, вызывается игровой узел _on_Character_health_depleted
:
# 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()
Вы можете отправлять вместе с сигналом столько аргументов, сколько хотите.
Вот пример, где это может быть полезным. Допустим, мы хотим, чтобы полоса здоровья на экране реагировала на изменения здоровья анимацией, но мы хотим, чтобы пользовательский интерфейс был отделен от игрока в нашем дереве сцен.
В нашем скрипте Character.gd
мы определяем сигнал health_changed
и отправляем его вместе с Object.emit_signal(), и узел Game
выше по дереву сцен мы соединяем с Lifebar
используя метод Object.connect():
# 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)
...
Примечание
Чтобы использовать сигналы, ваш класс должен расширить класс Object``или любой другой тип, расширяя его в виде ``Node
, KinematicBody
, Control
...
В узле Game
мы берём узлы Character
и Lifebar
, затем соединяем символ, отправляющий сигнал, с получателем, в нашем случае с узлом Lifebar
.
# 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")
Это позволяет Lifebar
реагировать на изменения здоровья без соединения с узлом Character
.
Вы можете написать дополнительные аргументы в скобках после определения сигнала:
# Defining a signal that forwards two arguments.
signal health_changed(old_value, new_value)
Эти аргументы показываются в доке узла редактора, и Godot может использовать их, чтобы производить для вас функции обратного вызова. Однако, вы всё ещё можете отправлять любое число аргументов при отправке сигналов; отправка правильных значений (и их проверок) зависит только от вас.

GDScript может связывать массив значений с соединениями между сигналом и методом. Когда сигнал отправляется, метод обратного вызова получает связанные значения. Эти связанные аргументы уникальны для каждого соединения, и значения останутся прежними.
Вы можете использовать это множество значений чтобы добавить дополнительную постоянную информацию в соединение, если сам отправляемый сигнал не даёт вам доступ ко всем данным, которые вам нужны.
Основываясь на предыдущем примере, представим, что мы хотим показать лог урона, полученного всеми персонажами на экране, вроде Игрок1 получил 22 единицы урона
. Сигнал health_changed
не выдаёт имя получившего урон персонажа. Так что, когда мы соединим сигнал с внутриигровой консолью, мы можем добавить имя персонажа в связку аргумента массива:
# 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])
Наш узел BattleLog
получает каждый элемент в связке массива как дополнительный аргумент:
# 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."
Сопрограммы с промежуточным возвратом (yield)¶
GDScript предлагает поддержку сопрограмм через встроенную функцию yield. Вызов yield()
немедленно произведет возврат из текущей функции с текущим замороженным состоянием этой же функции как возвращаемое значение. При вызове resume()
на данном результате объект продолжит выполнение и вернет все, что возвращает функция. После возобновления состояние объекта становится недействительным. Вот пример:
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.
Будет выведено:
Hello
my dear
world
Также можно передавать значения между функциями yield()
и resume()
, например:
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.
Будет выведено:
Hello
world
cheers!
Не забывайте сохранять новое состояние функции при использовании нескольких yield
:
func co_func():
for i in range(1, 5):
print("Turn %d" % i)
yield();
func _ready():
var co = co_func();
while co is GDScriptFunctionState && co.is_valid():
co = co.resume();
Сопрограммы и сигналы¶
Настоящая мощь использования yield
заключается в их сочетании с сигналами. yield
может принимать два аргумента, объект и сигнал. Когда сигнал будет получен, выполнение будет возобновлено. Вот несколько примеров:
# Resume execution the next frame.
yield(get_tree(), "idle_frame")
# Resume execution when animation is done playing.
yield(get_node("AnimationPlayer"), "animation_finished")
# Wait 5 seconds, then resume execution.
yield(get_tree().create_timer(5.0), "timeout")
Сами сопрограммы используют сигнал completed
при переходе в недействительное состояние, например:
func my_func():
yield(button_func(), "completed")
print("All buttons were pressed, hurray!")
func button_func():
yield($Button0, "pressed")
yield($Button1, "pressed")
my_func
будет продолжаться только после нажатия обеих кнопок.
Вы также можете получить аргумент сигнала, когда он передается объектом:
# Wait for when any node is added to the scene tree.
var node = yield(get_tree(), "node_added")
Если передано несколько аргументов, то yield
вернёт массив из этих аргументов:
signal done(input, processed)
func process_input(input):
print("Processing initialized")
yield(get_tree(), "idle_frame")
print("Waiting")
yield(get_tree(), "idle_frame")
emit_signal("done", input, "Processed " + input)
func _ready():
process_input("Test") # Prints: Processing initialized
var data = yield(self, "done") # Prints: waiting
print(data[1]) # Prints: Processed Test
Если вы не уверены, может ли функция вернуть промежуточный результат или нет, или может ли он вернуться несколько раз, вы можете привести промежуточный результат к сигналу completed
через условие:
func generate():
var result = rand_range(-1.0, 1.0)
if
Комментарии¶
Все, что находится после символа
#
до конца строки игнорируется и считается комментарием.# This is a comment.