Básicos do GDScript

Introdução

GDScript é uma linguagem de auto nível, dinamicamente tipada, usada para criar conteúdo. Utiliza uma sintaxe similar a do Python (blocos são baseados em indentação e muitas palavras-chave são similares). Seu objetivo é ser otimizada para e firmemente integrada ao Godot Engine, permitindo grande flexibilidade para a criação e integração de conteúdo.

Histórico

Nota

A documentação sobre a história do GDScript foi movida para o Frequently Asked Questions.

Exemplo de GDScript

Algumas pessoas conseguem aprender melhor observando a sintaxe, então aqui está um simples exemplo de como o GDScript parece.

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

Se você possui experiência prévia com linguagens estaticamente tipadas, como C, C++, ou C#, mas nunca utilizou uma linguagem dinamicamente tipada antes, é aconselhado que leia este tutorial: GDScript: Uma introdução às linguagens dinâmicas.

Idioma/Dialeto

A seguir, uma visão geral é dada sobre o GDScript. Detalhes, como quais métodos são disponíveis para arrays ou outros objetos, devem ser vistos nas descrições da classe relacionada.

Identificadores

Qualquer String que se restringe a caracteres alfabéticos (a a z e A a Z), dígitos (0 a 9) e _ se qualifica como um identificador. Além disso, identificadores não devem começar com um dígito. Identificadores são [i]case-sensitive[/i] (foo é diferente de FOO).

Palavras-chave

A seguir está a lista de palavras-chaves suportadas pela linguagem. Já que as palavras-chaves são palavras reservadas (código), elas não podem ser usadas como identificadores. Operadores (como in, not, and ou or) e nomes de tipos embutidos como os listados nas seções seguintes são também palavras reservadas.

Palavras chaves são definidas no tokenizador da GDScript no caso de você querer dar uma olhada nos bastidores.

Palavra-chave

Descrição

if

Veja if/else/elif.

elif

Veja if/else/elif.

else

Veja if/else/elif.

for

Veja for.

while

Veja while.

match

Veja match.

break

Sai da execução do atual laço for ou while.

continue

Pula imediatamente a próxima iteração do laço for ou while.

pass

Usado onde uma declaração é requerida sintaticamente, mas a execução do código é indesejada ,por exemplo em funções vazias.

return

Retorna um valor de uma função.

class

Define uma classe interna.

class_name

Define um nome de classe e um ícone opcional para o seu script.

extends

Define qual classe estender com a classe atual.

is

Testa se uma variável extende de uma classe dada, ou é de um certo tipo embutido.

as

Transmita o valor para um determinado tipo, se possível.

self

Refere-se a instância atual da classe.

tool

Executa o script no editor.

signal

Define um sinal.

func

Define uma função.

static

Define uma função estática. Variáveis membro estáticas não são permitidas.

const

Define uma constante.

enum

Define um enumerador.

var

Define uma variável.

onready

Inicializa uma variável quando o Nó ao qual o script está anexado e os seus filhos fizerem parte da árvore da cena.

export

Salva a variável junto com o recurso que ela está ligada e a torna visível e modificável no editor.

setget

Define funções setter e getter para uma variável.

breakpoint

Ajudante do Editor para debugar pontos de parada.

preload

Pré-carrega uma classe ou uma variável. Consulte Classes como recursos _.

yield

Suporte a corrotina. Consulte corrotinas com yield.

assert

Declara uma condição, erro de logs em caso de falha. Ignorado em compilações de não-depuração. Consulte Palavra-chave Assert _.

remote

Anotação de RPC em redes. Consulte :ref: documentos de multijogador de alto nível <doc_high_level_multiplayer>.

master

Anotação de RPC em redes. Consulte :ref: documentos de multijogador de alto nível <doc_high_level_multiplayer>.

puppet

Anotação de RPC em redes. Consulte :ref: documentos de multijogador de alto nível <doc_high_level_multiplayer>.

remotesync

Anotação de RPC em redes. Consulte :ref: documentos de multijogador de alto nível <doc_high_level_multiplayer>.

mastersync

Anotação de RPC em redes. Consulte :ref: documentos de multijogador de alto nível <doc_high_level_multiplayer>.

puppetsync

Anotação de RPC em redes. Consulte :ref: documentos de multijogador de alto nível <doc_high_level_multiplayer>.

PI

A constante PI.

TAU

A constante TAU.

INF

Constante de infinito. Usada para comparações.

NAN

Constante de NAN (não um número). Usada para comparações.

Operadores

A seguir, está a lista de operadores suportados e sua precedência.

Operador

Descrição

x[index]

Inscrição (prioridade máxima)

x.attribute

Referência de atributo

foo()

Chamada de função

is

Checagem de tipo de instância

~

NÃO Binário

-x

Negativo / Negação unária

* / %

Multiplicação / Divisão / Resto

Estes operadores têm o mesmo comportamento que a C++. A divisão inteira é truncada ao invés de devolver um número fracionário, e o operador % só está disponível para ints ("fmod" para flutuadores), e é usado adicionalmente para formatar Strings

+

Adição / Concatenação de matrizes

-

Subtração

<< >>

Deslocamento de bits

&

E Binário

^

OU EXCLUSIVO Binário

|

OU Binário

< > == != >= <=

Comparações

in

Quando usado com a palavra-chave if, ele verifica se um valor está dentro de uma string, lista, intervalo, dicionário ou nó. Quando usado com a palavra-chave for, é usado para iterar o conteúdo de uma string, lista, intervalo, dicionário ou nó.

! not

NÃO Booleano

and &&

E Booleano

or ||

OU Booleano

if x else

if/else Ternário

as

Conversão de tipo

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

Atribuição (menor prioridade)

Literais

Literal

Tipo

45

Inteiro Base 10

0x8f51

Inteiro de base 16 (hexadecimal)

0b101010

Inteiro de base 2 (binário)

3.14, 58.1e-10

Número de ponto flutuante (real)

"Olá", "Oi"

Strings

"""Olá"""

String com múltiplas linhas

@"Nodo/Rotulo"

NodePath ou StringName

$NodePath

Forma abreviada para get_node("NodePath")

Inteiros e flutuantes podem ter seus números separados por _ para torná-los mais legíveis. As seguintes formas de escrever números são todas válidas:

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.

Comentários

Qualquer coisa desde um # até o fim da linha é ignorada e é considerada como um comentário.

# This is a comment.

Tipos definidos por padrão

Tipos embutidos são alocados em pilha. Eles são passados como valores. Isso significa que uma cópia é criada em cada atribuição ou quando passadas como argumentos para funções. As únicas exceções são Arrays e Dictionaries, que são passados por referência e portanto são compartilhados. (Matrizes agrupadas como PoolByteArray ainda são passadas como valores.)

Tipos básicos definidos por padrão

Uma variável no GDScript pode ser atribuída a vários tipos definidos por padrão.

null

null é um tipo de dados vazio que não contém informação nenhuma e que não pode ser atribuído nenhum outro valor.

bool

Abreviação de "booleano", pode conter apenas true ou false.

int

Abreviação de "inteiro", armazena valores inteiros(positivos e negativos). Ele é armazenado como um valor de 64 bits, equivalente a "int64_t " em C++.

float

Armazena números reais, incluindo decimais, usando valores de ponto flutuante. Ele é armazenado como um valor de 64 bits, equivalente a "Double " em C++. Observação: atualmente, estruturas de dados, como Vector2, Vector3 e PoolRealArray armazenam 32 bit de ponto flutuante, ou seja 32 números após a virgula.

String

Uma sequência de caracteres no Formato Unicode. Strings podem conter as seguintes sequências de escape:

Sequência de escape

Se expande para

\n

Nova linha (feed de linha)

\t

Caractere tab horizontal

\r

Retorno de transporte

\a

Alerta (bipe/campainha)

\b

Retroceder

\f

Quebra de página do feed de formulário

\v

Caractere tab vertical

\"

Citação dupla

\'

Citação única

\\

Barra invertida

\uXXXX

Ponto de código Unicode XXXX (hexadecimal, sem sensibilidade de caixa)

GDScript também suporta Formatação de Strings em GDScript.

Tipos básicos de vetor

Vector2

O tipo vetor 2D contém os campos x e y. Também pode ser acessado como uma matriz.

Rect2

O tipo 2D Rectangle contém dois campos vetoriais: position e size. Contém também um campo end que é position + size.

Vector3

O tipo de vetor 3D contém os campos x, y e z. Isso também pode ser acessado como um array.

Transform2D

Matrix de 3x2 usada para transformações em 2D.

Plane

Tipo Plano 3D em formato normalizado que contém um campo vetorial "normal" e uma distância escalar "d".

Quat

Quaternion é um tipo de dado utilizado para representar uma rotação 3D. Isto é útil para interporlar rotações.

AABB

Caixa delimitadora alinhada por eixo (ou caixa 3D) contém 2 campos vetoriais: position e size. Contém também um campo end que é position + size.

Basis

Matrix 3x3 usada para rotação 3D e escala. Ela contem 3 campos vetoriais ("x", "y" e "z") e também pode ser acessada como um array de vetores 3D.

Transform

Transformadas 3D contem um campo Basis basis e um campo Vector3 origin.

Tipos definidos por padrão da Engine

Color

O tipo de dados Color contem campos r, g, b e a para vermelho, verde, azul e transparência respectivamente. E podem também ser acessados como h, s, e v para matiz, saturação e valor respectivamente.

NodePath

O caminho compilado para um nodo usado principalmente no sistema de cenas. Ele pode ser facilmente atribuído para, e a partir de, uma String.

RID

ID de Recurso (RID). Servidores usam RIDs genéricos para referenciar dados opacos.

Object

Classe base para qualquer coisa que não seja um tipo básico.

Tipos contêiner definidos por padrão

Array

Sequência genérica de tipos de objeto arbitrária, incluindo outras matrizes ou dicionários (veja abaixo). A matriz pode redimensionar dinamicamente. Matrizes são indexados iniciando do índice 0. Índices negativos contam do final.

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

Matrizes em GDScript são alocadas linearmente na memória para aumentar sua velocidade. Matrizes muito grandes (mais de dezenas de milhares de elementos) podem, no entanto, causar fragmentação de memória. Se isso for uma preocupação, existem tipos especiais de matriz. Estas aceitam apenas um único tipo de dados. Elas evitam a fragmentação de memória e também usam menos memória, mas são atômicas e tendem a ser mais lentas que as matrizes genéricas. Portanto, elas são recomendadas somente para grandes conjuntos de dados:

Dictionary

Contêiner associativo que armazena valores referenciados por chaves únicas.

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

Sintaxe de tabela no estilo Lua também é suportada. O estilo Lua usa = ao invés de : e não usa aspas para marcar chaves que são strings (diminuindo a quantidade de escrita). Note, no entanto, que como qualquer identificador GDScript, chaves escritas desta forma não podem começar com dígitos.

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

Para adicionar uma chave a um dicionário existente, acesse isso como uma chave existente e atribua a isso:

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

Nota

A sintaxe de brackets pode ser usada para acessar propriedades de qualquer Object, não apenas Dicionários. Tenha em mente que isso causará um erro de script ao tentar indexar uma propriedade inexistente Para evitar isto, use os métodos Object.get() e :ref:`Object.set() <class_Object_method_set>`em vez disso.

Dados

Variáveis

Variáveis podem existir como membros de classe ou locais em funções. Elas são criadas com a palavra-chave var e podem, opcionalmente, ser atribuídas com um valor durante a inicialização.

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.

As variáveis podem, opcionalmente, ter uma especificação de tipo. Quando um tipo é especificado, a variável será forçada a ter sempre esse mesmo tipo, e tentar atribuir um valor incompatível gerará um erro.

Os tipos são especificados na declaração da variável usando um símbolo : (dois-pontos) após o nome da variável, seguido pelo tipo.

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

Se a variável for inicializada dentro da declaração, o tipo pode ser inferido, então é possível omitir o nome do tipo:

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

A inferência de tipos só é possível se o valor atribuído tiver um tipo definido, caso contrário, irá gerar um erro.

Tipos válidos são:

  • Tipos embutidos (Array, Vector2, int, String, etc.).

  • Classes da engine (Node, Resource, Reference, etc.).

  • Nomes constantes se eles contiverem um recurso de script (MyScript se você declarar const MyScript = preload("res://my_script.gd")).

  • Outras classes no mesmo script, respeitando o escopo (InnerClass.NestedClass se você declarou classNestedClass dentro da classInnerClass no mesmo escopo).

  • Classes de script declaradas com a palavra-chave class_name.

Conversão

Valores atribuídos a variáveis digitadas devem ter um tipo compatível. Se for necessário forçar um valor a ser de um determinado tipo, em particular para tipos de objetos, você pode usar o operador de conversão as.

A conversão entre tipos de objetos resulta no mesmo objeto se o valor for do mesmo tipo ou um subtipo do tipo de conversão.

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

Se o valor não for um subtipo, a operação de conversão resultará em um valor `` null``.

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

Para tipos internos, eles serão convertidos à força, se possível, caso contrário, o mecanismo gerará um erro.

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.

Conversão também é útil para ter melhores variáveis de tipo seguro ao interagir com a árvore da cena:

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

Constantes

Constantes são valores que você não pode alterar quando o jogo está em execução. Seu valor deve ser conhecido na hora da compilação. Usar a palavra-chave const lhe permite dar um nome a um valor constante. Tentar atribuir uma valor a uma constante depois de declarada resultará em um erro.

Recomendamos usar constantes sempre que um valor não deve ser alterado.

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

Embora o tipo de constantes seja inferido a partir do valor atribuído, também é possível adicionar especificação de tipo explícito:

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

Atribuir um valor de um tipo incompatível gerará um erro.

Nota

Como matrizes e dicionários são passados por referência, as constantes são "planas". Isto significa que se você declarar um array ou dicionário constante, eles ainda podem ser modificados posteriormente. Eles não podem ser reatribuídos com outro valor no entanto.

Enumeradores

Enumeradores são basicamente alternativas para constantes, e são muito úteis se você quer atribuir inteiros consecutivos para alguma constante.

Se você passar um nome para um enum, ele irá colocar todas as chaves dentro de um dicionário constante com aquele nome.

Importante

A partir do Godot 3.1, chaves em um enum nomeado não são registradas como constantes globais. Elas devem ser acessadas com o prefixo do nome do enum (Name.KEY); veja no exemplo abaixo.

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.

Funções

Funções sempre pertencem a class. A prioridade de escopo para a procura de variáveis é: local → membro da classe → global. A variável self é sempre disponível e é fornecida como uma opção para acessar membros da classe, mas não é sempre necessária (e , diferente do Python, não deve ser enviada como o primeiro argumento de uma função).

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

Uma função pode retornar em qualquer ponto. O valor padrão de retorno é null.

Funções também podem ter especificação de tipo para os argumentos e para o valor de retorno. Tipos de argumentos podem ser adicionados de forma semelhante às variáveis:

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

Se um argumento de função tiver um valor padrão, é possível inferir o tipo:

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

O tipo de retorno da função pode ser especificado após a lista de argumentos usando o token de seta (->):

func my_int_function() -> int:
    return 0

Funções que possuem um tipo de retorno devem retornar um valor adequado. Definir o tipo como void significa que a função não retorna nada. As funções void podem retornar cedo com a palavra-chave return, mas não podem retornar nenhum valor.

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

Nota

Funções não void devem sempre retornar um valor, então se seu código possui instruções de ramificação (como um construtor if / else), todos os caminhos possíveis devem ter um retorno. Por exemplo, se você tiver um return dentro de um bloco if, mas não depois, o editor irá gerar um erro, porque se o bloco não for executado, a função não terá um valor válido para retornar.

Referenciando funções

Ao contrário do Python, funções não são objetos de primeira-classe em GDScript. Isso significa que elas não podem ser armazenadas em variáveis, passadas como argumento para outras funções ou retornadas de outras funções. Essas limitações são por motivos de performance.

Para referenciar uma função pelo nome durante o tempo de execução, (ex: para armazenar em uma variável, ou passar para uma outra função como um argumento) você precisa usar os auxiliares call ou 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)

Funções estáticas

Uma função pode ser declarada como estática. Quando uma função é estática, ela não possui acesso as variáveis de instância ou ao self. Isso é útil principalmente para a criação de bibliotecas de funções auxiliares:

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

Expressões e fluxo de controle

Expressões são padrão e podem ser atribuições, chamadas de função, estruturas de controle, etc (veja abaixo). ; como um separador de expressões é inteiramente opcional.

if/else/elif

Condições simples são criadas usando a sintaxe if/else/elif. Parênteses ao redor de condições são permitidos, mas não obrigatórios. Dada a natureza dos recuos baseados em tabs, elif pode ser usado ao invés de else/if para manter o nível de recuo.

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

Expressões curtas podem ser escritas na mesma linha da condição:

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

Às vezes, você pode querer atribuir um valor inicial diferente baseado em uma expressão booleana. Nesse caso, expressões do tipo if-ternário são úteis:

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

Expressões ternárias de if podem ser aninhadas para lidar com mais de 2 casos. Ao aninhar expressões ternárias de if é recomendado envolver a expressão completa em várias linhas para preservar a legibilidade:

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

Você também pode querer verificar se um valor está contido em algo. Você pode usar uma instrução if combinada com o operador in para fazer isso:

# 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

Para iterar através de uma faixa de valores, como em arrays ou tabelas, um loop for é usado. Quando iterando por um array, o elemento atual é armazenado na variável do loop. Ao iterar por um dicionário, a chave é armazenada na variável do loop.

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

Uma expressão match é usada para ramificar a execução de um programa. É equivalente à expressão switch encontrada em muitas outras linguagens de programação, mas oferece algumas funcionalidades adicionais.

Sintaxe básica:

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

Curso rápido para pessoas familiarizadas com expressões switch:

  1. Substitua switch por match.

  2. Remova case.

  3. Remova todos os breaks. Se você não quer interromper (break) por padrão, você pode usar continue para contornar.

  4. Troque default por um único sublinhado.

Controle de fluxo:

Os padrões são comparados de cima para baixo. Se um padrão coincide, o bloco correspondente é executado. Depois disso, a execução do código continua após a expressão match. Você pode usar continue para parar a execução no bloco atual e verificar se há uma correspondência adicional nos padrões abaixo dele.

Há 6 tipos de padrões:

  • Padrão de constante

    Constantes primitivas, como números e textos:

    match x:
        1:
            print("We are number one!")
        2:
            print("Two are better than one!")
        "test":
            print("Oh snap! It's a string!")
    
  • Padrão de variável

    Compara o conteúdo de uma variável/enumeração:

    match typeof(x):
        TYPE_REAL:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Padrão coringa

    Este padrão compara/coincide com tudo. É escrito com uma única sublinha _ .

    Pode ser usado como equivalente ao default de uma expressão switch em outras linguagens:

    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.")
    
  • Padrão de ligação

    Um padrão de ligação introduz uma nova variável. Como o padrão coringa, ele coincide com tudo – e ainda dá a esse valor um nome. É útil especialmente para padrões de matriz e de dicionário.

    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)
    
  • Padrão de matriz

    Compara com um matriz. Cada elemento do padrão é um padrão por si só, para que você possa aninhá-los.

    O comprimento da matriz é testado primeiro, ele tem que ter o mesmo tamanho do padrão, senão o padrão não coincide.

    Matriz sem término: Uma matriz pode ser maior que o padrão deixando como último subpadrão ...

    Todo subpadrão precisa ser separado por vírgulas.

    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")
    
  • Padrão de dicionário

    Funciona da mesma forma que o padrão de array. Toda chave precisa ser um padrão constante.

    O tamanho do dicionário é testado primeiro, ele tem que ser o mesmo tamanho do padrão, senão o padrão não coincide.

    Dicionário sem término: Um dicionário pode ser maior que o padrão deixando como último subpadrão ...

    Qualquer subpadrão precisa ser separado por vírgulas.

    Se você não especificar um valor, só a existência da chave é conferida.

    Um padrão de valor é separado do padrão de chave com : .

    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")
    
  • Multipadrões

    Você pode especificar múltiplos padrões separando-os por uma vírgula. Esses padrões não são permitidos de ter quaisquer ligações.

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

Classes

Por padrão, todos os arquivos de scripts são classes sem nome. Neste caso, você só irá poder os referenciar usando o caminho do arquivo, usando tanto um caminho relativo como um absoluto. Por exemplo, se você nomear um arquivo de script 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()

Registrando classes nomeadas

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

Aviso

Se o script estiver localizado no diretório res://addons/, class_name só fará o com que o nó apareca na caixa de diálogo Criar Novo Node se o script for parte de um plugin de editor ativado. Veja Fazendo plugins para mais informações.

Aqui está um exemplo de um arquivo de classe:

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

Nota

A sintaxe da classe do Godot é compacta: ela só pode conter variáveis membros ou funções. Você pode utilizar funções estáticas, mas não membros variáveis não-estáticas. Do mesmo modo, o motor inicializará variáveis toda vez que você criar uma instância, e isso inclui arrays e dicionários. Isso serve para segurança de threads, já que scripts podem ser inicializados em threads separados sem que o usuário saiba.

Herança

Uma classe (salva como um arquivo) pode herdar de:

  • Uma classe global.

  • Um outro arquivo de classe.

  • Uma classe interna dentro de outro arquivo de classe.

Herança múltipla não é permitida.

Herança usa a palavra-chave 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

Para verificar se uma certa instância herda de uma certa classe, a palavra-chave is (é) pode ser usada:

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

# [...]

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

Para chamar uma função em uma classe base (ex: um extend-ido em sua classe atual), insira um . no início do nome da função:

.base_func(args)

Isso é especialmente útil porque funções em classes extendidas substituem as funções com o mesmo nome em suas classes de base. Então se você ainda quiser chamá-los, você pode usar . no início (como a palavra-chave super em outras linguagens):

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

Nota

Funções padrão como _init, e a maioria das notificações como _enter_tree, _exit_tree, _process, _physics_process, etc. são chamadas em todas as classes base automaticamente. Não é necessário chamá-las explicitamente quando estiver sobrecarregando-as.

Construtor de classe

O construtor de classe, chamada ao criar uma instância da classe, é chamado de _init. Como mencionado anteriormente, os construtores de classes-pai são chamados automaticamente ao herdar uma classe. Então não costuma haver necessidade de chamar ._init() explicitamente.

Ao contrário da chamada de uma função normal, como no exemplo acima com .some_func, se o construtor da classe herdada tiver argumentos, serão passados da seguinte forma:

func _init(args).(parent_args):
   pass

Isso é melhor explicado através de exemplos. Suponha que tenhamos tal cenário:

# 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

Têm algumas coisas para manter em mente aqui:

  1. Se a classe herdada (State.gd) definir um construtor _init que precisa de argumentos (e nesse caso) então a classe que herda (Idle.gd) precisa definir um _init também, e passar os parâmetros apropriados para o _init de State.gd.

  2. Idle.gd pode ter um número diferente de argumentos que a classe base State.gd.

  3. No exemplo acima, e passado para o construtor de State.gd é o mesmo e que foi passado para o Idle.gd.

  4. Se o construtor _init de Idle.gd não precisa de argumentos, ele ainda precisa passar algum valor para a classe base State.gd, mesmo que ele não faça nada. Isso nos leva ao fato que você pode passar valores literais ao construtor também, não só variáveis. ex:

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

Classes internas

Um arquivo de classe pode conter classes internas. Elas são definidas usando a palavra-chave class. Instâncias podem ser criadas usando a função NomeDaClasse.new().

# Inside a class file.

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


    func print_value_of_a():
        print(a)


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

Classes como recursos

Classes armazenadas em arquivos são tratadas como resources. Elas podem ser carregadas do disco para acessá-las em outras classes. Isso é feito ou pela função load ou pela preload (veja abaixo). Instâncias de uma classe carregada como recurso são feitas chamando a função new no objeto da classe:

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

Exportações

Nota

A documentação sobre a exportação foi movida para Exports no GDScript.

Setters (setadores) /getters (coletores)

É muito comum ser útil saber quando uma variável membro de classe se altera por qualquer que seja o motivo. Também pode ser desejado encapsular seu acesso de alguma forma.

Para isso, a GDScript provê uma sintaxe de setter/getter usando a palavra-chave setget, usada diretamente depois de uma definição de variável:

var variable = value setget setterfunc, getterfunc

Sempre que o valor da variável for modificado por uma fonte externa (não por uso local na classe), a função setter (setterfunc acima) será chamada. Isso acontece antes do valor ser mudado. O setter precisa decidir o que fazer com o novo valor. Do mesmo modo, quando variable é acessado, a função getter (getterfunc acima) precisa retornar (return) o valor desejado. A seguir um exemplo:

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.

Qualquer uma das funções setter ou getter podem ser omitidas:

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

Setters e getters são úteis especialmente ao exportar variáveis para o editor em scripts de ferramentas ou plugins, para validação de entrada.

Como dito, acesso local não irá iniciar o setter e getter. Aqui temos uma ilustração disso:

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)

Modo de Ferramenta

Scripts, por padrão, não executam dentro do editor e apenas as propriedades exportadas podem ser modificadas. Em alguns casos, é desejável que eles rodem dentro do editor (enquanto eles não executem código do jogo ou evitem manualmente de fazer isso). Para isso, a palavra tool existe e deve ser colocada no topo do arquivo:

tool
extends Button


func _ready():
    print("Hello")

Veja Executando código no editor para mais informações.

Aviso

Seja cauteloso ao liberar nós com queue_free() ou free() em um script de ferramenta (especialmente o próprio dono do script). Como os scripts de ferramentas executam o seu código no editor, o seu uso indevido pode causar falhas no editor.

Gerenciamento de memória

Se uma classe herdar de Reference, as instâncias serão liberadas quando não estiverem mais em uso. Nenhum coletor de lixo existe, apenas a contagem de referência. Por padrão, todas as classes que não definem herança estendem de Reference. Se isto não for desejado, então uma classe deve herdar de Object manualmente e deve chamar instance.free(). Para evitar ciclos de referência que não podem ser liberados, uma função WeakRef é fornecida para criar referências fracas. Eis um exemplo:

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

Alternativamente, quando não estiver usando referências, o is_instance_valid (instance) pode ser usado para verificar se um objeto foi liberado.

Sinais

Sinais são um modo de enviar mensagens de um objeto para que outros objetos possam reagir. Crie sinais personalizados para uma classe usando a palavra-chave signal.

extends Node


# A signal named health_depleted.
signal health_depleted

Nota

Sinais são um mecanismo de Callback. Eles também preenchem o papel de observadores, um padrão de programação comum. Para mais informações, leia o tutorial Observer tutorial no ebook Game Programming Patterns.

Esses sinais podem ser conectados a métodos da mesma forma com que você conecta sinais embutidos de nós como botões ou RigidBodies.

No exemplo abaixo, conectamos o sinal health_depleted de um nó Character a um nó Game. Quando o nó Character emite o sinal, a função _on_Character_health_depleted é chamada no nó Game:

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

Você pode emitir quantos argumentos desejar com um sinal.

Aqui está um exemplo em que isso é útil. Digamos que queremos que uma barra de vida na tela reaja às mudanças de saúde com uma animação, mas queremos manter a interface do usuário separada do player em nossa árvore de cenas.

Em nosso script Character.gd, definimos um sinal health_changed e o emitimos com Object.emit_signal() e no nó Game acima na árvore de cenas, nós a conectamos à Lifebar usando o método 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)
...

Nota

Para usar sinais, sua classe precisa estender a classe do Object ou algum tipo que a estenda como Node, KinematicBody, Control...

No nó Game, obtemos ambos os nós Character e Lifebar, em seguida, conectamos o personagem, que emite o sinal ao receptor, o nó Lifebar nesse caso.

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

Isso permite que a Lifebar reaja às alterações de saúde sem acoplá-la ao nó Character.

Você pode escrever nomes de argumento opcionais entre parênteses após a definição do sinal:

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

Estes argumentos aparecem na aba Nó do editor, e o Godot pode usá-los para criar funções para você. Entretanto, você ainda pode emitir qualquer número de argumento quando você emite sinais; é você quem tem que emitir os valores corretos.

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

GDScript pode passar uma matriz de argumentos a conexões entre um sinal e um método. Quando o sinal é emitido, chamando o método conectado, os argumentos são dados ao método. Esses argumentos são específicos a cada conexão, e os valores ficarão iguais.

Você pode usar essa matriz de valores para adicionar informações extras constantes à conexão, se o próprio sinal emitido não der acesso a todos os dados necessários.

Com base no exemplo acima, vamos dizer que queremos exibir um log dos danos recebidos por cada personagem na tela, como Player1 levou 22 de dano.. O sinal health_changed não fornece o nome do personagem que levou dano. Então quando conectamos o sinal ao console in-game, podemos adicionar o nome do personagem no argumento da matriz de ligações:

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

Nosso nó BattleLog recebe cada elemento na matriz de vínculos como um argumento extra:

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

Corrotinas com yield

GDScript oferece suporte para corrotinas através do comando embutido yield. Chamar yield() irá sair imediatamente da função atual, com o estado atual congelado desta mesma função como valor de retorno. Chamar resume() neste objeto resultante irá continuar a execução da função original do ponto que parou e retornar o que quer que a função retorne. Assim que retomada a execução da função suspensa, o objeto de estado se torna inválido. Aqui está um exemplo:

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.

Imprimirá:

Hello
my dear
world

Também é possível passar valores entre yield() e resume(), por exemplo:

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.

Imprimirá:

Hello
world
cheers!

Lembre-se de salvar o estado da nova função, ao utilizar múltiplos yields:

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

Corrotinas e sinais

O verdadeiro poder de utilizar yield é quando combinado com sinais. yield pode aceitar até dois parâmetros, um objeto e um sinal. Quando o sinal é recebido, a execução irá recomeçar. Abaixo temos alguns exemplos:

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

As próprias corrotinas usam o sinal completed quando fazem a transição para um estado inválido, por exemplo:

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


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

my_func somente continuará a execução assim que ambos os botões forem pressionados.

Você também pode obter o argumento do sinal assim que ele for emitido por um objeto:

# Wait for when any node is added to the scene tree.
var node = yield(get_tree(), "node_added")

Se houver mais de um argumento, yield retorna um array contendo os argumentos:

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

Se você não tem certeza se uma função pode fazer yield ou não, ou se ela pode fazer yield várias vezes, você pode fazer yield ao sinal completed condicionalmente:

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

Isto garante que a função retorne o que deveria retornar, independentemente de as corrotinas terem sido usadas internamente. Observe que usar while seria redundante aqui, pois o sinal completed é apenas emitido quando a função não faz mais yield.

Palavra-chave onready

Ao usar nós, é comum desejar manter referências a partes da cena em uma variável. Como as cenas só podem ser configuradas ao entrar na árvore da cena ativa, os subnós só podem ser obtidos quando uma chamada para Node._ready() é feita.

var my_label


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

Isto pode ficar meio confuso, especialmente com muitos nós e referências externas. Para isso, GDScript tem a palavra-chave onready, que atrasa a inicialização da variável de um membro até que _ready() seja chamado. Ela pode substituir o código acima por uma única linha:

onready var my_label = get_node("MyLabel")

Palavra-chave assert

A palavra-chave assert pode ser usada para checar detalhes em compilações de depuração. Essas asserções são ignoradas em compilações sem depuração. Isso significa que a expressão passada como argumento não será avaliada num projeto exportado em modo de lançamento. Por esse motivo, elas não devem conter expressões que sejam importantes para o código funcionar. Caso contrário, o comportamento do script variará dependendo se está compilando em modo depuração ou não.

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

Se ocorrer um erro de asserção enquanto estiver no modo de edição, o projeto sera pausado.