GDScript — это высокоуровневый- ориентированный императивный язык программирования с постепенной типизацией, созданный для Godot. Он использует синтаксис на основе отступов, аналогичный таким языкам, как Python . Его цель — оптимизировать и тесно интегрировать с 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#, но вы никогда раньше не использовали динамически типизированный язык, рекомендуется прочитать это руководство: doc_gdscript_efficient.
Язык
Далее следует общий обзор GDScript. Подробную информацию, например, о доступных методах для массивов или других объектов, следует искать в описаниях связанных классов.
Идентификаторы
Любая строка, которая ограничивает себя алфавитными символами (от a
до z
и от A
до Z
), цифрами (от 0
до 9
) и _
, квалифицируется как идентификатор. Кроме того, идентификаторы не должны начинаться с цифры. Идентификаторы чувствительны к регистру (foo
отличается от FOO
).
Идентификаторы также могут содержать большинство символов Юникода, являющихся частью UAX#31 . Это позволяет использовать имена идентификаторов, написанные на языках, отличных от английского. Символы Юникода, которые считаются «смешивающимися» с символами ASCII и эмодзи, не допускаются в идентификаторах.
Ключевые слова
Ниже приведен список ключевых слов, поддерживаемых языком. Поскольку ключевые слова являются зарезервированными словами (токенами), они не могут использоваться в качестве идентификаторов. Операторы (например, in
, not
, and
или or
) и имена встроенных типов, перечисленные в следующих разделах, также зарезервированы.
Ключевые слова определены в токенизаторе GDScript, если вы хотите взглянуть под капот.
Ключевое слово | Описание |
---|---|
if | Смотрите if/else/elif. |
еlif | Смотрите if/else/elif. |
elsе | Смотрите if/else/elif. |
for | См. for. |
whilе | Смотрите while. |
mаtch | Смотрите match. |
breаk | Выход из выполнения текущего for или while циклов. |
continuе | Немедленный переход к следующей итерации for или while циклов. |
pass | Используется там, где наличие инструкции требуется синтаксически, но выполнение ее кода нежелательно. Например, в пустых функциях. |
return | Возвращает значение из функции. |
класс | Определяет класс. |
clаss_name | Определяет скрипт как глобально доступный класс с указанным именем. |
extеnds | Объявляет какой класс расширяет текущий класс. |
is | Проверяет, расширяет ли переменная данный класс, или принадлежит ли она данному встроенному типу. |
in | Проверяет, находится ли значение в строке, списке, диапазоне, словаре или узле. При использовании с for , он перебирает их вместо тестирования. |
as | Приводит значение к данному типу, если это возможно. |
sеlf | Ссылается на текущий экземпляр класса. |
signаl | Объявляет сигнал. |
funс | Объявляет функцию. |
statiс | Определяет статическую функцию или статическую переменную |
сonst | Объявляет константу. |
enum | Объявляет перечисление. |
var | Объявляет переменную. |
brеakpoint | Помощник редактора для точек останова отладчика. В отличие от точек останова, создаваемых щелчком мыши по желобу, breakpoint они сохраняются в самом скрипте. Это делает его постоянным на разных машинах при использовании контроля версий. |
prеload | Предварительно загружает класс или переменную. См. Классы как ресурсы. |
await | Ожидает завершения сигнала или сопрограммы. Ожидание сигналов или сопрограмм . |
yield | Ранее использовался для сопрограмм. Сохранено как ключевое слово для перехода. |
assеrt | Задает условие, регистрирует ошибку при сбое. Игнорируется в не отладочных сборках. См. Ключевое слово Assert. |
void | Used to represent that a function does not return any value. |
PI | Константа Пи. |
TAU | Константа Тау. |
INF | Infinity constant. Used for comparisons and as result of calculations. |
NAN | NAN (not a number) constant. Used as impossible result from calculations. |
Операторы
Далее приведен список поддерживаемых операторов и их приоритет.
Оператор | Описание |
---|---|
( ) | Группировка (высший приоритет)Круглые скобки на самом деле не являются оператором, но позволяют явно указать приоритет операции. |
x[index] | Подписка |
x.attribute | Ссылка на атрибут |
foo() | Вызов функции |
await x | Ожидание сигналов или сопрограмм |
x is Node | Проверка типаСм. также функцию is_instance_of() . |
x ** y | Возведение в степень Умножается x само на себя y , аналогично вызову функции pow() .Примечание. В GDScript ** оператор является левоассоциативным . Подробное примечание смотрите после таблицы. |
~x | Побитовое НЕ |
+x-x | Идентичность/Отрицание |
x * y | Умножение / Деление / Остаток от деленияОператор % дополнительно используется для строк формата .Примечание. Эти операторы ведут себя так же, как C++, что может быть неожиданным для пользователей, использующих Python, JavaScript и т. д. См. подробное примечание после таблицы. |
x + y | Сложение (или конкатенация)/вычитание |
x << y | Битовый сдвиг |
x & y | Побитовое И |
x ^ y | Побитовое Исключающее ИЛИ |
x | y | Побитовое ИЛИ |
x == y | СравнениеПодробное примечание смотрите после таблицы. |
x in y | Проверка включенияin также используется с ключевым словом for как часть синтаксиса. |
not x!x | Логическое NOT и его нерекомендуемый псевдоним |
x and y | Логическое И и его нерекомендуемый псевдоним |
x or y | Логическое ИЛИ и его нерекомендуемый псевдоним |
true_expr if cond | Тернарный оператор Если/Иначе (if/else) |
x as Node | Тип литья |
x = y | Присваивание (низший приоритет) Вы не можете использовать оператор присваивания внутри выражения. |
Примечание
Поведение некоторых операторов может отличаться от ожидаемого:
- Если оба операнда оператора
/
имеют тип int , то вместо дробного выполняется целочисленное деление. Например5 / 2 == 2
, . Если это нежелательно, используйте хотя бы один литерал с плавающей запятой (x / 2.0
), приведение (float(x) / y
) или умножение на (x * 1.0 / y
). - Оператор
%
доступен только для целых чисел, для чисел с плавающей запятой используйте функцию fmod() . - Для отрицательных значений
%
оператор иfmod()
использует усечение вместо округления в сторону отрицательной бесконечности. Это означает, что остаток имеет знак. Если вам нужен остаток в математическом смысле, используйте вместо него функции posmod() и fposmod() . - Оператор
**
левоассоциативный . Это означает, что2 ** 2 ** 3
равно(2 ** 2) **
3 Используйте круглые скобки, чтобы явно указать необходимый вам приоритет, например2 ** (2 ** 3)
- Операторы
==
и!=
иногда позволяют сравнивать значения разных типов (например,1 == 1.0
true), но в других случаях это может вызвать ошибку во время выполнения. Если вы не уверены в типах операндов, вы можете смело использовать функцию is_same() (но учтите, что она более строга в отношении типов и ссылок). Для сравнения чисел с плавающей точкой вместо этого используйте функции is_equal_approx() и is_zero_approx() .
Литералы
Литерал | Тип |
45 | Целое число в десятичной системе счисления |
0x8f51 | Основание 16 (шестнадцатеричное) целое число |
0b101010 | Целое число в двоичной системе счисления |
3.14 , 58.1e-10 | Число с плавающей точкой (вещественное число) |
"Hello" ,'Hi' | Строки |
"""Привет""" | Многострочная строка |
&"name" | StringName |
^"Node/Label" | Путь узла NodePath |
$NodePath | Сокращение для get_node("NodePath") |
%UniqueNode | Сокращение от get_node(“%UniqueNode”) |
Типы данных 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.
Аннотации
В GDScript есть специальные токены, которые действуют как ключевые слова, но не являются ими, а являются аннотациями . Каждая аннотация начинается с @
символа и указывается именем. Подробное описание и пример каждой аннотации можно найти внутри справочника по классу GDScript .
Аннотации влияют на то, как сценарий обрабатывается внешними инструментами, и обычно не меняют его поведения.
В Godot члены класса можно экспортировать. Это означает, что их значение сохраняется вместе с ресурсом (например, сценой ), к которому они привязаны. Они также будут доступны для редактирования в редакторе свойств. Экспорт осуществляется с использованием @export
аннотации. Аналог [SerializeField] в юнити
Например, вы можете использовать его для экспорта значения в редактор:
@export_range(1, 100, 1, "or_greater")
var ranged_var: int = 50
Дополнительные сведения об экспорте свойств см. в статье об экспорте GDScript .
Любое константное выражение, совместимое с требуемым типом аргумента, может быть передано как значение аргумента аннотации:
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
аннотация
Значения для этих свойств не назначаются сразу при инициализации узла ( Object._init ), а вычисляются и сохраняются непосредственно перед Node._ready .
Иногда нужно сохранить ссылки на части сцены в переменной. Поскольку конфигурирование сцен гарантировано только при входе в активное дерево сцены, дочерние узлы могут быть получены только при вызове функции 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.
Продолжение линии
Строку кода в GDScript можно продолжить на следующей строке, используя обратную косую черту ( \
). Добавьте один в конце строки, и код в следующей строке будет вести себя так, как будто там находится обратная косая черта. Вот пример:
var a = 1 + \
2
Строку можно продолжать несколько раз, например:
var a = 1 + \
4 + \
10 + \
4
Встроенные типы
Встроенные типы выделяются в стеке. Они передаются как значения. Это означает, что копия создается при каждом назначении или при передаче их в качестве аргументов функциям. Исключением являются Object
, Array
, Dictionary
и упакованные массивы (например PackedByteArray
), которые передаются по ссылке, поэтому они являются общими. Все массивы Dictionary
и некоторые объекты ( Node
, Resource
) имеют duplicate()
метод, позволяющий сделать копию.
Базовые встроенные типы
Переменная в GDScript может быть определена несколькими встроенными типами.
null
null
– это пустой тип данных, который не содержит никакой информации и не может принимать другие значения.
bool
Сокращенно от “boolean”, оно может содержать только true
или false
.
int
Сокращенно от «целое число», оно хранит целые числа (положительные и отрицательные). Он хранится как 64-битное значение, что эквивалентно int64_t
C++.
float
Сохраняет действительные числа, включая десятичные, используя значения с плавающей запятой. Он хранится как 64-битное значение, что эквивалентно double
C++. Примечание. В настоящее время структуры данных, такие как Vector2
, Vector3
и PackedFloat32Array
хранят 32-битные значения одинарной точности float
.
String
Последовательность символов в формате Unicode . Строковые литералы могут содержать следующие escape-последовательности:
Escape-последовательность | Означает |
\n | Новая строка (перевод строки) |
\t | Символ горизонтальной табуляции |
\r | Возврат каретки |
\a | Оповещение (звуковой сигнал/звонок) |
\b | Возврат |
\f | Разрыв страницы |
\v | Символ вертикальной табуляции |
\" | Двойная кавычка |
\' | Одиночная кавычка |
\\ | Обратная косая черта |
\uXXXX | Код Unicode UTF-16 XXXX (шестнадцатеричный, без учета регистра) |
\UXXXXXX | Код Unicode UTF-32 XXXXXX (шестнадцатеричный, без учета регистра) |
Существует два способа представления экранированного символа Юникода выше 0xFFFF:
- как UTF-16 surrogate pair
\uXXXX\uXXXX
. - как одиночный код UTF-32
\UXXXXXX
.
Кроме того, использование \
символа новой строки внутри строки позволит вам продолжить ее на следующей строке без вставки символа новой строки в саму строку.
GDScript также поддерживает строки формата .
StringName
Неизменяемая строка, допускающая только один экземпляр каждого имени. Они создаются медленнее и могут привести к ожиданию блокировок при многопоточности. Зато их очень быстро сравнивать, что делает их хорошими кандидатами на роль ключей словаря.
NodePath
Предварительно проанализированный путь к узлу или свойству узла. Его можно легко присвоить строке и из нее. Они полезны для взаимодействия с деревом для получения узла или для воздействия на свойства, например, с помощью Tweens .
Векторные встроенные типы
Vector2
2D-векторный тип, содержащий поля x
и y
. Также может быть доступен как массив.
Vector2i
То же, что и Vector2, но компоненты являются целыми числами. Полезно для представления элементов в 2D-сетке.
Rect2
2D Прямоугольник, содержащий два поля векторов: position
и size
. Альтернативно содержит поле end
, которое представляет собой position + size
.
Vector3
3D векторный тип, содержащий поля x
, y
и z
. Может быть получен как массив.
Vector3r
То же, что и Vector3, но компоненты являются целыми числами. Может использоваться для индексации элементов в 3D-сетке.
Transform2D
Матрица 3×2, используемая для 2D-преобразований.
Plane
Тип трехмерной плоскости в нормализованной форме, которая содержит normal
векторное поле и d
скалярное расстояние.
Quaternion
Quaternion – это тип данных, используемый для представления трехмерного вращения. Полезно для интерполяции вращений.
AABB
Выровненная по оси ограничительная рамка (или трехмерная коробка) содержит 2 поля типа Vector: position
и size
. Также содержит поле end
, которое является position+size
.
Basis
Матрица 3×3, используемая для трехмерного вращения и масштабирования. Он содержит 3 векторных поля (x
, y
и z
), а также доступен в виде массива трехмерных векторов.
Transform3D
3D-преобразование содержит поле Basis basis
и поле Vector3 origin
.
Встроенные типы движка
Color
Color – тип данных цвета, содержит поля r
, g
, b
, и a
. Он также доступен как h
, s
, и v
для оттенка(hue)/насыщенности(saturation)/значения(value).
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].
Типизированные массивы
В 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]
Array
и Array[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 выделяются в памяти линейно для повышения скорости работы. Однако большие массивы (более десятков тысяч элементов) могут привести к фрагментации памяти. Если это проблема, то доступны специальные типы массивов. Они принимают только один тип данных. Они избегают фрагментации памяти и также потребляют меньше памяти, но являются атомными и, как правило, работают медленнее, чем обычные массивы. Поэтому их рекомендуется использовать только для больших наборов данных:
- PackedByteArray : массив байтов (целые числа от 0 до 255).
- PackedInt32Array : массив 32-битных целых чисел.
- PackedInt64Array : массив 64-битных целых чисел.
- PackedFloat32Array : массив 32-битных чисел с плавающей запятой.
- PackedFloat64Array : массив 64-битных чисел с плавающей запятой.
- PackedStringArray : массив строк.
- PackedVector2Array : массив значений Vector2 .
- PackedVector3Array : массив значений Vector3 .
- PackedColorArray : массив значений цвета .
Словарь – 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().
Сигнал
Сигнал — это сообщение, которое может быть отправлено объектом тем, кто хочет его услышать. Тип сигнала можно использовать для передачи излучателя.
Сигналы лучше использовать, получая их от реальных объектов, например $Button.button_up
.
Callable – возможность вызова
Содержит объект и функцию, что полезно для передачи функций в качестве значений (например, при подключении к сигналам).
Получение метода в качестве члена возвращает вызываемый объект. установит значение вызываемого объекта в качестве объекта и метода.var 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, etc.).
- Константы, если они содержат скрипт ресурса (
MyScript
если вы объявилиconst MyScript = preload("res://my_script.gd")
). - Другие классы в том же скрипте, учитывающие область видимости (
InnerClass.NestedClass
если вы объявилисlass NestedClass
внутрисlass InnerClass
в той же области видимости). - Классы объявленные в скрипте с ключевым словом
class_name
. - Автозагрузки зарегистрированы как синглтоны.
Примечание
Хотя Variant
это допустимая спецификация типа, это не настоящий тип. Это всего лишь означает, что нет заданного типа, что эквивалентно отсутствию статического типа вообще. Таким образом, вывод не разрешен по умолчанию для Variant
, поскольку это, скорее всего, ошибка.
Вы можете отключить эту проверку или сделать ее только предупреждением, изменив ее в настройках проекта. Подробности см. в разделе Система предупреждений GDScript .
Статические переменные
Переменная-член класса может быть объявлена статической:
static var a
Статические переменные принадлежат классу, а не экземплярам. Это означает, что статические переменные разделяют значения между несколькими экземплярами, в отличие от обычных переменных-членов.
Изнутри класса вы можете получить доступ к статическим переменным из любой функции, как статической, так и нестатической. Извне класса вы можете получить доступ к статическим переменным, используя класс или экземпляр (второй вариант не рекомендуется, поскольку он менее читаем).
Примечание
Аннотации @export
и @onready
нельзя применять к статической переменной. Локальные переменные не могут быть статическими.
В следующем примере определяется Person
класс со статической переменной с именем max_id
. Мы увеличиваем max_id
in _init()
функцию. Это позволяет легко отслеживать количество 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
Статические переменные могут иметь подсказки типа, методы установки и получения:
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 , если статические переменные не хранят важные данные и могут быть сброшены.
Предупреждение
В настоящее время из-за ошибки скрипты никогда не освобождаются, даже если @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.
Приведение также полезно для получения более надежных типов переменных при взаимодействии с деревом:
# 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 {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
Если вы передадите имя перечислению, оно поместит все ключи в словарь констант с этим именем. Это означает, что все константные методы словаря также можно использовать с именованным перечислением.
Важно
Ключи в именованном перечислении не регистрируются как глобальные константы. Доступ к ним должен начинаться с префикса имени перечисления ( 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())
Функции
Функции всегда принадлежат классу ` <Classes>`_ . Приоритет области видимости для поиска переменной следующий: локальная → член класса → глобальная. Переменная 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 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
означает, что функция ничего не возвращает. Пустые функции могут возвращаться раньше с ключевым словом return
, но они не могут возвращать значения.
func void_function() -> void:
return # Can't return a value.
Примечание
Не пустые (Non-void) функции должны всегда возвращать значение, поэтому если в вашем коде есть ветвистые выражения (такие как конструкция if
/else
), все возможные пути должны иметь ответвление. Например, если return
внутри блока if
, но не после него, то редактор выдаст ошибку, потому что если условие не будет выполнено, то функция не будет иметь действительного значения для возврата.
Ссылочные функции
Функции являются первоклассными элементами с точки зрения объекта 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
метода. Вы не можете использовать ()
оператор напрямую. Такое поведение реализовано, чтобы избежать проблем с производительностью при прямых вызовах функций.
Лямбда-функции
Лямбда-функции позволяют объявлять функции, не принадлежащие классу. Вместо этого создается объект Callable , который напрямую присваивается переменной. Это может быть полезно для создания вызываемых объектов для передачи без загрязнения области действия класса.
var lambda = func(x): print(x)
lambda.call(42) # Prints "42"
Лямбда-функциям можно присвоить имена в целях отладки:
var lambda = func my_lambda(x):
print(x)
Лямбда-функции фиксируют локальную среду. Локальные переменные передаются по значению, поэтому они не будут обновляться в лямбде, если они будут изменены в локальной функции:
var x = 42
var my_lambda = func(): print(x)
my_lambda.call() # Prints "42"
x = "Hello"
my_lambda.call() # Prints "42"
Примечание
Значения внешней области ведут себя как константы. Следовательно, если вы объявите массив или словарь, его потом все равно можно будет изменить.
Статические функции
Функция может быть объявлена статической. Когда функция является статической, она не имеет доступа к переменным-членам экземпляра или self
. Статическая функция имеет доступ к статическим переменным. Также статические функции полезны для создания библиотек вспомогательных функций:
static func sum2(a, b):
return a + b
Лямбда-выражения не могут быть объявлены статическими.
См. также Статические переменные и Статический конструктор .
Операторы и контроль потока
Операторы являются стандартными и могут быть присваиваниями, вызовами функций, структурами управления потоком и т.д. (см. ниже). Разделитель ;
является абсолютно необязательным.
Выражения
Выражения представляют собой упорядоченные последовательности операторов и их операндов. Выражение само по себе также может быть оператором, хотя в качестве операторов разумно использовать только вызовы, поскольку другие выражения не имеют побочных эффектов.
Выражения возвращают значения, которые можно присвоить допустимым целям. Операндами некоторого оператора могут быть другие выражения. Присвоение не является выражением и, следовательно, не возвращает никакого значения.
Вот несколько примеров выражений:
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/elsе/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
Вы также можете проверить, содержится ли внутри чего-либо значение. Для этого вы можете использовать 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!")
whilе
Простые циклы создаются с использованием 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
предлагает некоторые дополнительные возможности.
Основной синтаксис:
match (expression):
[pattern](s):
[block]
[pattern](s):
[block]
[pattern](s):
[block]
Предупреждение
match
является более строгим по типу, чем ==
оператор. Например, 1 и 1.0 не будут соответствовать . Единственным исключением является сопоставление : например, String "hello"
соответствует StringName &"hello"
.
Ускоренный курс для людей, знакомых с правилами switch:
- Заменители .
switch
_match
- Уберите
case
. - Удалите все
break
s. - Смените
default
на единичное подчеркивание.
Управление потоком:
Узоры совмещаются сверху вниз. Если шаблон совпадает, будет выполнен первый соответствующий блок. После этого выполнение продолжается ниже match
оператора.
Примечание
Особое continue
поведение, match
поддерживаемое в версии 3.x, было удалено в Godot 4.0.
Существует 6 типов шаблонов:
1. Постоянный шаблон
Постоянные (константные) примитивы, такие как числа и строки
match x:
1:
print("We are number one!")
2:
print("Two are better than one!")
"test":
print("Oh snap! It's a string!")
2.Шаблон переменной
Соответствует содержимому переменной/перечисления(enum):
match typeof(x):
TYPE_FLOAT:
print("float")
TYPE_STRING:
print("text")
TYPE_ARRAY:
print("array")
3.Шаблон подстановки
Этот шаблон подходит для любого поиска. Это пишется как единичное подчёркивание.
Он может быть использован как эквивалент выражения 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.")
4. Шаблон привязки
Шаблон привязки вводит новую переменную. Как и шаблон подстановки, он соответствует всему, а также присваивает этому значению имя. Это особенно полезно в шаблонах массивов и словарей:
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)
5. Шаблон массива
Соответствует массиву. Каждый отдельный элемент шаблона массива сам по себе является шаблоном, поэтому вы можете их вкладывать.
Сначала проверяется длина массива, она должна быть того же размера, что и шаблон, в противном случае шаблон не совпадет.
Открытый массив: Массив может быть больше, чем массив, создавая последнюю подшаблонку ..
.
Каждый подшаблон должен быть разделен запятыми.
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")
6. Шаблон словаря
Работает так же, как шаблон массива. Каждый ключ должен быть постоянным шаблоном.
Сначала проверяется размер словаря, он должен быть того же размера, что и шаблон, в противном случае шаблон не совпадет.
Открытый словарь: Словарь может быть больше шаблона, если сделать последний подшаблоном
– ..
.
Каждый подшаблон должен быть разделен запятыми.
Если вы не указываете значение, то проверяется только наличие ключа.
Шаблон значения отделяется от шаблона ключа символом :
.
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")
7. Несколько шаблонов
Также можно указать несколько разных шаблонов, разделенных запятой. Эти шаблоны не должны иметь никаких привязок в них.
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
ключевое слово. При желании вы можете использовать @icon
аннотацию с путем к изображению, чтобы использовать его в качестве значка. После этого ваш класс появится в редакторе с новым значком:
# item.gd
@icon("res://interface/icons/item.png")
class_name Item
extends Node

Вот пример файла класса:
# 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 инициализирует нестатические переменные каждый раз, когда вы создаете экземпляр, включая массивы и словари. Это соответствует принципам потокобезопасности, поскольку сценарии могут инициализироваться в отдельных потоках без ведома пользователя.
Наследование
Класс (хранимый как файл) может наследоваться от:
- Глобального класса.
- Другого файла класса.
- Внутреннего класса внутри другого файла класса.
Множественное наследование невозможно.
При наследовании использует ключевое слово 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
Примечание
Если наследование не определено явно, класс по умолчанию будет наследовать 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
:
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
Здесь есть несколько вещей, которые необходимо держать в голове:
- Если унаследованный класс (
state.gd
) определяет_init
конструктор, который принимает аргументы (e
в данном случае), то наследующий класс (idle.gd
) также должен определить и передать соответствующие параметры в from ._init
_init
state.gd
idle.gd
может иметь другое количество аргументов, чем базовый классstate.gd
.- В приведенном выше примере
e
конструктору передаетсяstate.gd
то же самое,e
что иidle.gd
. - Если
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
. Создать экземпляр можно с помощью функции 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 предоставляет специальный синтаксис для определения свойств с использованием ключевых слов set
и get
после объявления переменной. Затем вы можете определить блок кода, который будет выполняться при доступе к переменной или ее назначении.
Пример:
var milliseconds: int = 0
var seconds: int:
get:
return milliseconds / 1000
set(value):
milliseconds = value * 1000
Использование имени переменной для установки ее внутри собственного метода установки или для получения ее внутри собственного метода получения приведет к непосредственному доступу к базовому члену, поэтому он не будет генерировать бесконечную рекурсию и избавит вас от явного объявления другой переменной:
signal changed(new_value)
var warns_when_changed = "some value":
get:
return warns_when_changed
set(value):
changed.emit(value)
warns_when_changed = value
Эта переменная-член поддержки не создается, если вы ее не используете.
Примечание
В отличие set get
от предыдущих версий Godot, методы установки и получения свойств вызываются всегда , даже если к ним обращаются внутри одного и того же класса (с префиксом или без него self.
). Это делает поведение последовательным. Если вам нужен прямой доступ к значению, используйте другую переменную для прямого доступа и заставьте код свойства использовать это имя.
Если вы хотите разделить код объявления переменной или вам нужно разделить код на несколько свойств, вы можете использовать другую нотацию для использования существующих функций класса:
var my_prop:
get = get_my_prop, set = set_my_prop
Это также можно сделать в той же строке.
Режим Инструмента
По умолчанию скрипты не запускаются внутри редактора, и можно изменять только экспортированные свойства. В некоторых случаях желательно, чтобы они запускались внутри редактора (при условии, что они не выполняют код игры или не избегают этого вручную). Для этого @tool
аннотация существует и должна быть размещена вверху файла:
@tool
extends Button
func _ready():
print("Hello")
Предупреждение
Будьте осторожны при освобождении узлов с помощью queue_free()
или free()
в скрипт инструмента (особенно самого владельца скрипта). Так как скрипт инструмента запускает свой код в редакторе, неправильное их использование может привести к крашу редактора.
Управление памятью
Godot реализует подсчет ссылок для освобождения определенных экземпляров, которые больше не используются вместо сборщика мусора или требуют чисто ручного управления. Любой экземпляр класса RefCounted (или любого класса, который его наследует, например Resource ), будет автоматически освобожден, когда он больше не будет использоваться. Экземпляр любого класса, который не является RefCounted (например, Node или базовый тип объекта ), будет оставаться в памяти до тех пор, пока не будет удален с помощью free()
(или queue_free()
для узлов).
Примечание
Если узел удаляется с помощью free()
или queue_free()
, все его дочерние элементы также будут рекурсивно удалены.
Чтобы избежать ссылочных циклов, которые невозможно освободить, предусмотрена функция WeakRef для создания слабых ссылок, которые позволяют получить доступ к объекту, не препятствуя освобождению 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(instance) может быть использован для проверки того, был ли освобожден объект.
Сигналы
Сигналы это способ отправки уведомлений от объекта, на которые могут реагировать другие объекты. Чтобы создать собственные сигналы для класса, используйте ключевое слово signal
.
extends Node
# A signal named health_depleted.
signal health_depleted
Примечание
Сигналы это Callback механизм. Они также выполняют роль наблюдателя, распространенный шаблон программирования. Для лучшего понимания информации, читайте про шаблон наблюдатель в словаре Шаблонов Игрового Программирования.
Вы можете подключить эти сигналы к методам так же, как вы подключаете встроенные сигналы узлов, таких как Button или RigidBody3D .
В приведенном ниже примере мы подключаем health_depleted
сигнал от Character
узла к Game
узлу. Когда узел излучает сигнал, вызывается Character
игровой узел :_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
сигнал и излучаем его с помощью Signal.emit() , а из Game
узла выше нашего дерева сцены мы подключаем его к Lifebar
методу Signal.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.
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
мы берём узлы Character
и Lifebar
, затем соединяем символ, отправляющий сигнал, с получателем, в нашем случае с узлом 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)
Эти аргументы показываются в доке узла редактора, и 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.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
слово можно использовать для создания сопрограмм , которые ждут, пока не будет выдан сигнал, прежде чем продолжить выполнение. Использование 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
становится сопрограммой, а это означает, что вызывающему абоненту также нужно ее дождаться:
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")
Примечание
В отличие yield
от предыдущих версий Godot, вы не можете получить объект состояния функции. Это сделано для обеспечения безопасности типов. При наличии такой безопасности типов функция не может сказать, что она возвращает значение, 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!")
Источник:
https://docs.godotengine.org/ru/4.x/tutorials/scripting/gdscript/gdscript_basics.html