Scripting

Introdução

Antes do Godot 3.0, a única maneira de roteirizar um jogo era usar doc_gdscript. Agora, Godot tem quatro (sim, quatro!) linguagens oficiais e a habilidade de adicionar linguagens de roteirização extras dinamicamente!

Isso é ótimo, principalmente por causa da enorme flexibilidade oferecida, mas também dificulta nosso trabalho de dar suporte a linguagens.

As linguagens “principais” no Godot, entretanto, são a GDScript e a VisualScript. O motivo principal de escolhê-las é seu nível de integração com o Godot, já que isso torna a experiência mais suave; ambas têm uma integração boa com o editor, enquanto que C# e C++ precisam ser editadas em uma IDE separada. Se você é fã de linguagens digitadas estaticamente, vá de C# ou C++.

GDScript

GDScript é, como já mencionado, a principal linguagem usada no Godot. Usá-la tem alguns pontos positivos quando comparada às demais dada sua alta integração com o Godot:

  • É simples, elegante e projetada para ser familiar aos usuários de outras linguagens como Lua, Python, Squirrel, etc.
  • Carrega e compila rapidamente.
  • A integração com o editor é agradável de trabalhar, com completação de código para nós, sinais e muitos outros itens pertinentes à cena em edição.
  • Tem tipos de vetores embutidos (como Vectors, transformações, etc.), tornando-a eficiente para uso pesado de álgebra linear.
  • Suporte a múltiplas threads tão eficientemente quanto como em linguagens tipadas estaticamente – uma das limitações que nos fez evitar máquinas virtuais como Lua, Squirrel, etc.
  • Não usa coletor de lixo, então ele troca um pouquinho de automação (a maioria dos objetos usa referencias contadas, de qualquer forma) por determinismo.
  • Sua natureza dinâmica torna fácil otimizar seções de código em C++ (via GDNative) se mais desempenho for necessário, sem ter que recompilar o motor de jogo.

Se está indeciso e tiver experiência com programação, especialmente com linguagens tipada dinamicamente, opte por GDScript!

VisualScript

A partir da versão 3.0, o Godot oferece Visual Scripting. Esta é uma implementação típica de uma linguagem de “blocos e conexões”, mas adaptada para o jeito que o Godot funciona.

Roteirização visual é uma ótima ferramenta para não programadores, ou mesmo para desenvolvedores experientes que queira tornar partes do código mais acessíveis para outros, como projetistas de jogos e artistas.

Também pode ser usado por programadores para construir máquinas de estado ou fluxos de trabalhos de nós visuais personalizados - por exemplo, um sistema de diálogo.

.NET / C#

Como a C# da Microsoft é favorita entre desenvolvedores de jogos, adicionamos suporte oficial para ela. C# é uma linguagem madura, com toneladas de código escritos para ela, e cujo suporte foi adicionado graças a uma generosa doação da Microsoft.

Ela tem uma excelente relação de compromisso entre desempenho e facilidade de uso, embora tenha que se atentar para seu coletor de lixo.

Já que o Godot usa plataforma .NET Mono, em teoria qualquer biblioteca ou infraestrutura .NET terceirizada pode ser usada para roteirizar no Godot, assim como qualquer linguagem de programação compatível com a Infraestrutura de Linguagem Comum (CLI), tais como F#, Boo ou ClojureCLR. Na prática, entretanto, C# é a única opção .NET com suporte oficial.

GDNative / C++

Finalmente, uma das nossas adições mais brilhantes à versão 3.0: GDNative permite roteirizar em C++ sem precisar recompilar (ou mesmo reiniciar) o Godot.

Qualquer versão da C++ pode ser usada, e misturar marcas e versões de compiladores para as bibliotecas compartilhadas geradas funciona perfeitamente; tudo graças ao nosso uso de uma ponte de API C interna.

Esta linguagem é a melhor escolha para performance e não precisa ser usada no jogo todo, já que outras partes podem ser escritas em GDScript ou Visual Script. Entretanto, a API é limpa e fácil de usar, porque se assemelha, em sua maior parte, à API C++ real do Godot.

Mais linguagens podem ser disponibilizadas através da interface GDNative, mas tenha em mente que não damos suporte oficial a elas.

Roteirizando uma cena

No resto deste tutorial, configuraremos uma cena de interface gráfica contendo um botão e um rótulo, em que pressionar um botão altera o rótulo. Isto será demonstrado:

  • Escrever um roteiro e anexá-lo a um nó.
  • Conectar elementos de interface via sinais.
  • Escrever um roteiro que pode acessar outros nós na cena.

Antes de continuar, por favor leia a referência da GDScript. É uma linguagem projetada para ser simples e a sua referência é curta, então não levará mais que poucos minutos para ter uma visão geral dos conceitos.

Configuração da cena

Use o diálogo “Adicionar nó filho” acessível a partir da aba Cena (ou pressionando Ctrl+A) para criar uma hierarquia com os seguintes nós:

  • Painel
    • Rótulo
    • Botão

A árvore da cena deveria se parecer assim:

../../_images/scripting_scene_tree.png

Use o editor 2D para posicionar e redimensionar o Botão (Button) e o Rótulo (Label) para que eles se pareçam com a imagem a seguir. Você pode definir o texto a partir da aba Inspetor.

../../_images/label_button_example.png

Por fim, salve a cena com um nome como digaoi.tscn.

Adicionando um script

Clique com o botão direito do mouse no nó Painel e selecione “Adicionar um Script” a partir do menu de contexto:

../../_images/add_script.png

O diálogo de criação de roteiro irá aparecer. Este diálogo permite configurar a linguagem do roteiro, o nome da classe e outras opções relevantes.

Na GDScript, o arquivo em si representa a classe, então o campo de nome da classe não é editável.

O nó a que estamos anexando o arquivo é um painel, então o campo Herda de será preenchido automaticamente com “Panel”. Isso é o que queremos, já que o objetivo do roteiro é estender a funcionalidade do nosso nó painel.

Finalmente, informe o nome do caminho para o roteiro e selecione Criar:

../../_images/script_create.png

O roteiro será, então, criado e adicionado ao nó. Você pode vê-lo com um ícone “Abrir script” ao lado do nó na aba Cena, assim como na propriedade Script no Inspetor:

../../_images/script_added.png

Para editar o script, selecione um desses botões, ambos destacados na imagem acima. Isso lhe levará ao editor de scripts, onde um modelo padrão será incluído:

../../_images/script_template.png

Não há muito por aqui. A função _ready() é chamada quando o nó, e todos os seus filhos, entrar na cena ativa. Nota: _ready() não é um construtor; o construtor é, na verdade, a função _init().

O papel do roteiro

Um roteiro adiciona comportamento a um nó. É usado para controlar como o nó funciona e também como ele interage com outros nós: filhos, pais, irmãos e assim por diante. O escopo local do roteiro é o nó. Em outras palavras, o roteiro herda as funções fornecidas por tal nó.

../../_images/brainslug.jpg

Manipulando um sinal

Sinais são “emitidos” quando algum tipo específico de ação acontece, e eles podem ser conectados a qualquer função de qualquer instância de script. Sinais são usados majoritariamente em nós de interface gráfica – embora outros nós também os tenha – e você pode até mesmo definir sinais personalizados em seus próprios roteiros.

Neste passo, conectaremos o sinal “pressed” (“pressionado”) a uma função personalizada. Formar conexões é a primeira etapa e definir a função personalizada é a segunda. Para o primeiro estágio, o Godot provê duas maneiras de criar conexões: através da interface visual que o editor possui ou através de código.

Embora iremos usar o método do código para o resto desta série de tutoriais, vamos cobrir aqui como a interface do editor funciona para referência futura.

Selecione o nó Botão na árvore da cena e selecione o guia “Nó”. Em seguida, assegure-se que selecionou “Sinais”.

../../_images/signals.png

Se você agora selecionar “pressed()” dentro de “BaseButton” e clicar no botão “Conectar…” no canto inferior direito, você abrirá o diálogo de criação de conexão.

../../_images/connect_dialogue.png

No canto inferior direito, estão as coisas principais necessárias para criar uma conexão: um nó que implementa o método que você quer disparar (representado aqui como um NodePath – “Caminho para o nó”) e o nome do método a ser disparado.

A seção superior esquerda mostra uma lista com nós da sua cena, com o nome do nó emissor destacado em vermelho. Selecione o nó “Painel” aqui. Quando selecionar um nó, o “Caminho para o nó” na parte de baixo será atualizado automaticamente para descrever um caminho relativo do nó emissor para o selecionado.

By default, the method name will contain the emitting node’s name (“Button” in this case), resulting in _on_[EmitterNode]_[signal_name]. If you do have the “Make Function” check button checked, then the editor will generate the function for you before setting up the connection.

E isso conclui o guia sobre como usar a interface visual. Entretanto, isso é um tutorial de script, então, em nome do aprendizado, vamos mergulhar no processo manual!

Para conseguir isso, nós lhe apresentaremos uma função que é provavelmente a mais usada por programadores Godot: Node.get_node(). Essa função usa caminhos para obter nós em qualquer lugar na cena, relativos ao nó que possui o roteiro.

Por conveniência, apague tudo abaixo de extends Panel. Você preencherá o resto do roteiro à mão.

Pelo fato de Botão e Rótulo serem irmãos entre si e filhos de Painel, aonde o roteiro foi anexado, você pode obter o Botão digitando a seguinte função _ready():

func _ready():
    get_node("Button")
public override void _Ready()
{
    GetNode("Button");
}

Em seguida, escreva uma função que será chamada quando o botão for pressionado:

func _on_Button_pressed():
    get_node("Label").text = "HELLO!"
public void _OnButtonPressed()
{
    GetNode<Label>("Label").Text = "HELLO!";
}

Finalmente, conecte o sinal “pressionado” do botão à função _ready() usando Object.connect().

func _ready():
    get_node("Button").connect("pressed", self, "_on_Button_pressed")
public override void _Ready()
{
    GetNode("Button").Connect("pressed", this, nameof(_OnButtonPressed));
}

O roteiro final deveria se parecer com isto:

extends Panel

func _ready():
    get_node("Button").connect("pressed", self, "_on_Button_pressed")

func _on_Button_pressed():
    get_node("Label").text = "HELLO!"
using Godot;

// IMPORTANT: the name of the class MUST match the filename exactly.
// this is case sensitive!
public class sayhello : Panel
{
    public override void _Ready()
    {
        GetNode("Button").Connect("pressed", this, nameof(_OnButtonPressed));
    }

    public void _OnButtonPressed()
    {
        GetNode<Label>("Label").Text = "HELLO!";
    }
}

Execute a cena e pressione o botão. Você deveria obter o seguinte resultado:

../../_images/scripting_hello.png

Uau, olha ssó! Parabéns por roteirizar sua primeira cena.

Nota

Um engano comum ao acompanhar este tutorial é como get_node(caminho) funciona. Para um certo nó, get_node(caminho) busca em seus filhos imediatos. No código acima, isso significa que Botão deve ser um filho de Painel. Se Botão fosse na verdade filho de Rótulo, o código para obtê-lo seria:

# Not for this case,
# but just in case.
get_node("Label/Button")
// Not for this case,
// but just in case.
GetNode("Label/Button")

Além disso, lembre-se de que os nós são referenciados por nome, e não por tipo.

Nota

O painel à direita do diálogo de conexão é para ligar valores específicos aos parâmetros da função conectada. Você pode adicionar e remover valores de diferentes tipos.

A abordagem por código também permite isso com um quarto parâmetro do tipo Array, que é vazio por padrão. Sinta-se livre para ler a referência do método Object.connect` para mais informações.