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 principal motivo para 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 estaticamente tipadas, 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 é uma 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 é clara e fácil de usar, porque se assemelha, em sua maior parte, à API C++ 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.

Before continuing, make sure to skim and bookmark the GDScript reference. It's a language designed to be simple, and the reference is structured into sections to make it easier to get an overview of the concepts.

Configuração da cena

If you still have the "instancing" project open from the previous tutorial, then close that out (Project -> Quit to Project List) and create a New Project.

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

A parte superior da caixa de diálogo exibe uma lista dos nós da cena com o nome do nó emissor destacado em azul. Selecione o nó "Painel" aqui.

A parte inferior da caixa de diálogo mostra o nome do método que será criado. Por padrão, o nome do método conterá o nome do nó emissor ("Button" neste caso), resultando em `` _on_[EmitterNode]_[signal_name]``.

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

The 'advanced' panel of the connect dialogue is for binding specific values to the connected function's parameters. You can add and remove values of different types.

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.