Основи GDScript

Вступ

GDScript - мова програмування високого рівня динамічного типу, що використовується для створення контенту. Вона використовує синтаксис, схожий на Python (блоки засновані на відступах, і багато ключових слів, схожі). Її мета - оптимізація та тісна інтеграція з Godot Engine, що забезпечує велику гнучкість для створення та інтеграції контенту.

Історія

Примітка

Документація про історію GDScript була переміщена до FAQ.

Зразок 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#, але раніше ви ніколи не використовували динамічно типізовані, радимо прочитати цей урок: GDScript: Вступ до мов динамічного типу.

Мова

Далі йде швидкий огляд GDScript. Деталі, наприклад, які методи доступні для масивів, чи інших об'єктів, слід шукати в описах пов'язаних класів.

Ідентифікатори

Будь-який рядок, що обмежується алфавітними символами ( від a до z і від A до Z), цифрами (від 0 до 9) і _ кваліфікується як ідентифікатор. Крім того, ідентифікатори не повинні починатися з цифри. Ідентифікатори залежать від регістру (foo відрізняється від FOO).

Ключові слова

Далі наведено список ключових слів, які підтримуються мовою. Оскільки ключові слова є зарезервованими словами (лексемами), їх не можна використовувати як ідентифікатори. Оператори (такі як in, not, and, або or) і імена вбудованих типів, перерахованих в наступних розділах, також зарезервовані.

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

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

Опис

if

Дивіться if/else/elif.

elif

Дивіться if/else/elif.

else

Дивіться if/else/elif.

for

Дивіться for.

while

Дивіться while.

match

Дивіться match.

break

Перериває виконання поточного циклу for, або while.

continue

Негайний перехід до наступної ітерації циклу for, або while.

pass

Використовується там, де, за правилами синтаксису, обов'язкова наявність інструкції (коду), але виконання коду небажане, наприклад, у порожніх функціях.

return

Повертає значення з функції.

клас

Визначає внутрішній клас.

class_name

Визначає назву класу та необов’язковий значок для вашого скрипту.

extends

Визначає який клас розширити поточним класом.

is

Тестує, чи змінна поширюється на заданий клас, чи на даний вбудований тип.

as

Якщо можливо, то переводить значення в заданий тип.

self

Посилається на поточний екземпляр класу.

tool

Виконує скрипт у редакторі.

signal

Визначає (оголошує) сигнал.

func

Визначає (оголошує) функцію.

static

Визначає статичну функцію. Статичні змінні члена не надані.

const

Визначає константу.

enum

Визначає перерахунок.

var

Визначає змінну.

onready

Ініціалізує змінну, як тільки Вузол додається до скрипту, і його нащадки є частиною дерева сцен.

export

Зберігає змінну разом з ресурсом, до якого вона додається, і робить її видимою та дозволяє її модифікувати, в редакторі.

setget

Визначає функції setter та getter для змінної.

breakpoint

Помічник редактора для налагодження контрольних точок.

preload

Попередньо завантажує клас, чи змінну. Дивіться Classes as resources.

yield

Підтримка співпрограм. Дивіться Співпрограми з виводом.

assert

Затверджує умову, реєструє помилку під час збою. Ігнорується в не-налагоджених збірках. Дивіться Assert keyword.

remote

Мережна анотація RPC. Дивіться документацію по багатокористувацькому режиму високого рівня.

master

Мережна анотація RPC. Дивіться документацію по багатокористувацькому режиму високого рівня.

puppet

Мережна анотація RPC. Дивіться документацію по багатокористувацькому режиму високого рівня.

remotesync

Мережна анотація RPC. Дивіться документацію по багатокористувацькому режиму високого рівня.

mastersync

Мережна анотація RPC. Дивіться документацію по багатокористувацькому режиму високого рівня.

puppetsync

Мережна анотація RPC. Дивіться документацію по багатокористувацькому режиму високого рівня.

PI

Константа Пі (π).

TAU

Константа Тау (τ).

INF

Константа нескінченності. Використовується для порівняння.

NAN

Константа NAN (not a number - не число). Використовується для порівнянь.

Оператори

Далі наведено список підтримуваних операторів та їх пріоритет.

Оператор

Опис

x[індекс]

Підписка (найвищий пріоритет)

x.attribute

Посилання на атрибут

foo()

Виклик функції

is

Перевірка типу екземпляра

~

Побітове NOT (НЕ)

-x

Негативне / Унарне заперечення

* / %

Множення/Ділення/Залишок ділення

These operators have the same behavior as C++. Integer division is truncated rather than returning a fractional number, and the % operator is only available for ints ("fmod" for floats), and is additionally used for Format Strings

+

Додавання / Об'єднання масивів

-

Віднімання

<< >>

Побітовий зсув (зміщення)

&

Побітове AND (І)

^

Побітове XOR (Виключене АБО)

|

Побітове OR (АБО)

< > == != >= <=

Порівняння

in

When used with the if keyword it checks if a value is within a string, list, range, dictionary, or node. When used with the for keyword it is used to iterate though the contents of a string, list, range, dictionary or node.

! not

Логічне NOT (НІ)

and &&

Логічне AND (І)

or ||

Логічне OR (АБО)

if x else

Потрійне if/else (якщо/інакше)

as

Type casting

= += -= *= /= %= &= |= <<= >>=

Присвоювання значення, найнижчий пріоритет

Літерали

Літерал

Тип

45

Ціле число за десятковою системою числення

0x8f51

Ціле число на основі шістнадцяткової системи числення

0b101010

Ціле число в двійковій (бінарній) системі числення

3.14, 58.1e-10

Число з рухомою комою (дійсне)

"Hello", "Привіт"

Рядки

"""Hello, Dude"""

Багаторядковий рядок (текст)

@"Node/Label"

NodePath або StringName

$NodePath

Скорочення для get_node("NodePath")

Цілі та десяткові числа можуть розділятись символом _ для кращої читабельності. Усі наступні способи запису чисел є дійсними:

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

Коментарі

Все що знаходиться після символа #, і до кінця рядка, ігнорується і вважається коментарем.

# This is a comment.

Вбудовані типи

Вбудовані типи виділяються стеком. Вони передаються як значення. Це означає, що їх копії створюються для кожного завдання, або при передачі їх, як аргументів, функціям. Єдині винятки - це Array (Масиви) і Dictionaries (Словники), які передаються посиланням, щоб бути спільними. (Об'єднані масиви, такі як PoolByteArray, не входять в число винятків і передаються як значення.)

Базові вбудовані типи

Змінна в GDScript може бути призначена декільком вбудованим типам.

нуль

null це порожній тип даних, який не містить інформації та не може приймати інші значення.

bool (логічні)

Скорочене для "boolean", може містити лише true, або false.

int

Скорочене від "integer" ("ціле число"), воно зберігає цілі числа (позитивні та негативні). Воно зберігається як 64-бітове значення, еквівалент "int64_t" в C ++.

float

Зберігає дійсні числа, включаючи десяткові дроби, використовуючи значення з рухомою комою. Зберігається як 64-бітове значення, еквівалентне "double" в C ++. Примітка: В даний час структури даних, такі як Vector2, Vector3 і PoolRealArray, зберігають 32-бітні одноточні значення "float".

String

Послідовність символів у форматі Unicode. Рядки (текст) можуть містити наступні escape послідовності:

Escape послідовності

Розширюються

\n

Новий рядок (перенесення рядка)

\t

Горизонтальний символ табуляції

\r

Повернення каретки

\a

Попередження (звуковий сигнал/дзвінок)

\b

Стерти

\f

Formfeed page break

\v

Вертикальний символ табуляції

\"

Подвійні лапки

\'

Одинарні лапки

\\

Зворотний слеш

\uXXXX

Символ Unicode XXXX (шіснадцятковий, без урахування регістру)

GDScript також підтримує Форматований текст GDScript.

Векторні вбудовані типи

Vector2

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

Rect2

Тип Прямокутник 2D, містить два векторних поля: position і size. Також містить поле end, яке є .position + size.

Vector3

Тип 3D вектора, містить поля x, y і z. Його також можна отримати як масив.

Transform2D

3 × 2 матриця, що використовується для 2D перетворень.

Plane

Тип 3D площини в нормалізованому вигляді, містить векторне поле normal і скалярну відстань d.

Quat

Quaternion - це тип даних, який використовується для представлення тривимірного обертання. Корисний для інтерполяції обертів.

AABB

Вісь вирівняна обмежувальною коробкою (або 3D коробкою) містить 2 векторних поля: position і size. Також містить поле end, яке є position + size.

Basis

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

Transform

3D перетворення містить поле Basis (Основа) basis``та поле Vector3 ``origin.

Вбудовані типи движка

Color

Color (Колір) тип даних містить поля r, g, b і a. Також можуть бути доступні h, s і v для hue(відтінок)/ saturation(насиченість) / value(значення).

NodePath

Упорядкований шлях до вузла, що використовується в основному в системі сцен. Його можна легко присвоювати рядку (тексту) і з нього.

RID

Ідентифікатор ресурсу (RID). Сервери використовують загальні RID для посилання непрозорих даних.

Object

Базовий клас для всього, що не є вбудованим типом.

Вбудовані типи контейнерів

Array

Масиви - це послідовність об'єктів довільних типів, включаючи інші масиви, чи словники (дивіться нижче). Масив може динамічно змінювати розмір. Масиви індексуються, починаючи з індексу 0. Негативні показники рахуються з кінця.

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

Масиви GDScript розподіляються в пам'яті лінійно для швидкості. Однак великі масиви (на більше десятків тисяч елементів) можуть спричинити фрагментацію пам'яті. Якщо це викликає занепокоєння, доступні спеціальні типи масивів. Вони приймають лише один тип даних. Вони уникають фрагментації пам'яті і використовують менше пам'яті, але є атомними і мають тенденцію працювати повільніше, ніж загальні масиви. Тому їх рекомендується використовувати лише для великих наборів даних:

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, тощо).

  • Імена констант, якщо вони містять ресурс скрипту (MyScript якщо ви визначили const MyScript = preload("res://my_script.gd")).

  • Інші класи в тому ж скрипті, з дотриманням меж застосування (InnerClass.NestedClass якщо ви визначили class NestedClass всередині class InnerClass в тих самих межах).

  • Класи скриптів, оголошені за допомогою ключового слова class_name.

Casting

Значення, присвоєні типізованим змінним, повинні мати сумісний тип. Якщо потрібно примусити значення бути певного типу, зокрема для типів об'єктів, ви можете використовувати оператор 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.

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

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

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

Константи

Константи - це значення, які ви не можете змінити під час роботи гри. Їх значення має бути відоме під час компіляції. Використання ключового слова const дозволяє надати значенню константи назву. Спроба присвоїти значення константі після її оголошення буде давати вам помилку.

Ми рекомендуємо використовувати константи для значень які не призначені для зміни.

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

Призначення значення несумісного типу призведе до помилки.

Примітка

Оскільки масиви та словники передаються за посиланням, константи є "плоскими". Це означає, що якщо ви оголосите в якості константи масив, чи словник, його все ще можна буде змінити згодом. Однак їх не можна замінити іншим значенням.

Переліки

Переліки - це, в основному, скорочення для констант, і вони дуже корисні, якщо ви хочете призначити послідовні цілі числа якимось константам.

Якщо ви передасте ім'я до переліку, він помістить усі ключі всередину постійного словника цього імені.

Важливо

У Godot 3.1, і пізніших версіях, ключі в названому переліку не реєструються як глобальні константи. До них слід звертатися з префіксом імені переліку (Ім'я.КЛЮЧ); дивіться приклад нижче.

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.

Функції

Функції завжди належать до класу<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 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

Примітка

Функції, які повертають інший тип, завжди повинні повертати якесь значення, тому якщо у вашому коді є операції розгалуження (наприклад конструкцію 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)

Статичні функції

Функція може бути оголошена статичною. Коли функція статична, вона не має доступу до змінних членів екземпляра (instance), або 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

Іноді, можливо, ви захочете призначити інше початкове значення на основі булевого виразу. У цьому випадку стають у нагоді потрійні вирази:

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

Ternary-if expressions can be nested to handle more than 2 cases. When nesting ternary-if expressions, it is recommended to wrap the complete expression over multiple lines to preserve readability:

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

Simple loops are created by using while syntax. Loops can be broken using break or continued using continue (i.e. skipping to the next iteration of the loop without executing any further code in the current iteration):

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

Оператор використовується для гілкування. Це еквівалент оператора switch, який можна знайти в багатьох інших мовах, але``match`` пропонує деякі додаткові функції.

Основний синтаксис:

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

Прискорений курс для людей, які знайомі із switch:

  1. Замініть switch на match.

  2. Видаліть case.

  3. Видаліть будь-які break. Якщо ви не хочете видаляти break ви можете використовувати continue щоб перескакувати їх.

  4. Змініть 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!")
    
  • Змінний шаблон

    Відповідає вмісту змінної / переліку:

    match typeof(x):
        TYPE_REAL:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Шаблон Wildcard

    Цей шаблон відповідає усьому. Він прописаний як окреме підкреслення.

    Він може бути використаний в якості еквівалента 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.")
    
  • Зв'язувальний шаблон

    Зв'язувальний шаблон вводить нову змінну. Як і шаблон wildcard, він відповідає всім - а також надає цьому значенню ім'я. Це особливо корисно для моделей масивів та словників:

    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)
    
  • Шаблон масиву

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

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

    Масив відкритого типу: Масив може стати більшим за шаблон, під час виконання останнього під-шаблону ...

    Кожен під-шаблон повинен бути розділений комою.

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

Registering named classes

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

# Item.gd

extends Node
class_name Item, "res://interface/icons/item.png"
../../../_images/class_name_editor_register_example.png

Попередження

Якщо скрипт знаходиться в каталозі res://addons/, вузол class_name з’явиться у діалоговому вікні Створити новий вузол лише в тому випадку, якщо скрипт є частиною увімкненого плагіна редактора. Дивіться Створення додатків для додаткової інформації.

Ось зразок файла класу:

# 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

Тут слід зазначити таке:

  1. Якщо успадкований клас (предок) (State.gd) визначає конструктор _init, який приймає аргументи (в даному випадку e), то спадковий клас (нащадок) (Idle.gd) повинен також визначити _init і передати відповідні параметри в _init з State.gd.

  2. Idle.gd може мати іншу кількість аргументів, ніж батьківський клас State.gd.

  3. У наведеному вище прикладі e передане конструктору State.gd те саме, що й e передане в Idle.gd.

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

    # 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 модифікується зовнішнім джерелом (тобто не локально всередині класі), буде викликана функція (setterfunc вище). Це відбувається до зміни значення. setter повинен вирішити , що робити з новим значенням. І навпаки, коли 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.

Будь-яку з функцій сетера, або гетера, можна опустити:

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

Додаткову інформацію дивіться в Running code in the editor.

Попередження

Будьте обережні, звільняючи вузли за допомогою queue_free(), або free(), в скрипті інструмента (особливо самого власника скрипту). Оскільки скрипти інструментів виконують свій код у редакторі, неправильне використання може призвести до збою редактора.

Управління пам'яттю

Якщо клас успадковується від 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 . Вони також виконують роль спостерігачів, загальна схема програмування. Для отримання додаткової інформації читайте Observer tutorial у книзі "Шаблони програмування ігор".

Ви можете підключити ці сигнали до методів так само, як ви підключаєте вбудовані сигнали вузлів, таких як Button, або RigidBody.

У наведеному нижче прикладі ми підключаємо сигнал health_depleted від вузла Character до вузла . Коли вузол Character випромінює сигнал, з вузла Game викликається _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 може використовувати їх для створення функцій зворотного виклику для вас. Однак ви можете випромінювати будь-яку кількість аргументів, коли ви випромінюєте сигнали; Ви повинні випромінювати правильні значення.

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

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

Coroutines with yield

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

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


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

Буде виведено:

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

Cпівпроцедури та сигнали

Справжня сила використання 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")

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

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


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

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

Ви також можете отримати аргумент сигналу, коли його випромінює об'єкт:

# 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

If you're unsure whether a function may yield or not, or whether it may yield multiple times, you can yield to the completed signal conditionally:

func generate():
    var result = rand_range(-1.0, 1.0)

    if result < 0.0:
        yield(get_tree(), "idle_frame")

    return result


func make():
    var result = generate()

    if result is GDScriptFunctionState: # Still working.
        result = yield(result, "completed")

    return result

Це гарантує, що функція повертає те, що вона повинна була повернути, незалежно від того, чи використовувались coroutines програми всередині. Завбачте, що використання тут while було б зайвим, оскільки сигнал completed випромінюється лише тоді, коли функція більше не дає результату.

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

При використанні вузлів бажання зберігати посилання на частини сцени в змінній цілком нормальне. Оскільки сцени можуть бути налаштовані лише під час входу в дерево активних сцен, під-вузли можна отримати лише тоді, коли здійснюється виклик Node._ready().

var my_label


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

Це може стати трохи громіздким, особливо коли вузли та зовнішні посилання накопичуються. Для цього у GDScript є ключове слово onready, яке відкладає ініціалізацію змінної члена до виклику _ready(). Воно може замінити вищевказаний код одним рядком:

onready var my_label = get_node("MyLabel")

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

Ключове слово assert може бути використано для перевірки умов в налагоджувальних збірках. Ці твердження ігноруються у не налагоджуваних збірках. Це означає, що вираз, переданий як аргумент, не буде оцінено в проєкті, експортованому в режимі випуску. Через це твердження повинні не містити виразів, які мають побічні ефекти. В іншому випадку поведінка скрипту може змінюватись залежно від того в якій збірці запускається проєкт.

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

Під час запуску проєкту з редактора проєкт буде призупинено, якщо станеться помилка твердження.