Guia de Estilo GDScript

Este guia de estilo lista as convenções utilizadas para escrever código GDScript elegante. O objetivo é encorajar a escrita de código limpo e legível, além de promover a consistência entre projetos, discussões e tutoriais. Esperamos que isto também encoraje a criação de ferramentas de auto-formatação.

Já que o GDScript é próximo da linguagem Python, este guia é inspirado no guia de estilos PEP 8 do Python.

Guias de estilo não são regras. Às vezes, você talvez não poderá aplicar algumas das diretrizes abaixo. Quando isso acontecer, use seu melhor julgamento, e peça opiniões de outros desenvolvedores.

Em geral, manter seu código consistente em seus projetos e em seu time é mais importante que seguir este guia à risca.

Nota

O editor de scripts padrão do Godot usa várias dessas convenções por padrão. Deixe que isso te ajude.

Aqui está um exemplo completo baseado nessas regras:

class_name StateMachine
extends Node
# Hierarchical State machine for the player.
# Initializes states and delegates engine callbacks
# (_physics_process, _unhandled_input) to the state.


signal state_changed(previous, new)

export var initial_state = NodePath()
var is_active = true setget set_is_active

onready var _state = get_node(initial_state) setget set_state
onready var _state_name = _state.name


func _init():
    add_to_group("state_machine")


func _ready():
    connect("state_changed", self, "_on_state_changed")
    _state.enter()


func _unhandled_input(event):
    _state.unhandled_input(event)


func _physics_process(delta):
    _state.physics_process(delta)


func transition_to(target_state_path, msg={}):
    if not has_node(target_state_path):
        return

    var target_state = get_node(target_state_path)
    assert(target_state.is_composite == false)

    _state.exit()
    self._state = target_state
    _state.enter(msg)
    Events.emit_signal("player_state_changed", _state.name)


func set_is_active(value):
    is_active = value
    set_physics_process(value)
    set_process_unhandled_input(value)
    set_block_signals(not value)


func set_state(value):
    _state = value
    _state_name = _state.name


func _on_state_changed(previous, new):
    print("state changed")
    emit_signal("state_changed")

Formatação

Codificação e caracteres especiais

  • Use caracteres line feed (LF) para quebrar linhas, não CRLF ou CR. (padrão do editor)

  • Use um caractere line feed no final de cada arquivo. (padrão do editor)

  • Use a codificação UTF-8 sem uma marca de ordem de byte. (padrão do editor)

  • Use Tabs ao invés de espaços para indentação. (padrão do editor)

Indentação

Cada nível de indentação deve ser uma unidade maior que a do bloco que o contém.

Bom:

for i in range(10):
    print("hello")

Ruim:

for i in range(10):
  print("hello")

for i in range(10):
        print("hello")

Use 2 níveis de indentação para distinguir linhas contínuas de blocos de código regulares.

Bom:

effect.interpolate_property(sprite, "transform/scale",
            sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
            Tween.TRANS_QUAD, Tween.EASE_OUT)

Ruim:

effect.interpolate_property(sprite, "transform/scale",
    sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
    Tween.TRANS_QUAD, Tween.EASE_OUT)

Exceções a essa regra são matrizes, dicionários e enums. Use apenas um nível de indentação para distinguir linhas contínuas:

Bom:

var party = [
    "Godot",
    "Godette",
    "Steve",
]

var character_dir = {
    "Name": "Bob",
    "Age": 27,
    "Job": "Mechanic",
}

enum Tiles {
    TILE_BRICK,
    TILE_FLOOR,
    TILE_SPIKE,
    TILE_TELEPORT,
}

Ruim:

var party = [
        "Godot",
        "Godette",
        "Steve",
]

var character_dir = {
        "Name": "Bob",
        "Age": 27,
        "Job": "Mechanic",
}

enum Tiles {
        TILE_BRICK,
        TILE_FLOOR,
        TILE_SPIKE,
        TILE_TELEPORT,
}

Vírgula extra

Use uma vírgula extra na última linha em matrizes, dicionários, e enums. Isso resulta em refatoração mais fácil e melhores amostras de diferenças em controle de versão, já que a última linha não precisa ser modificada ao adicionar novos elementos.

Bom:

enum Tiles {
    TILE_BRICK,
    TILE_FLOOR,
    TILE_SPIKE,
    TILE_TELEPORT,
}

Ruim:

enum Tiles {
    TILE_BRICK,
    TILE_FLOOR,
    TILE_SPIKE,
    TILE_TELEPORT
}

Vírgulas extras não desnecessárias em listas de uma linha só, então não adicione nesse caso.

Bom:

enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}

Ruim:

enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT,}

Linhas em branco

Deixe uma linha vazia em cima e embaixo de definições de classes e funções:

func heal(amount):
    health += amount
    health = min(health, max_health)
    emit_signal("health_changed", health)


func take_damage(amount, effect=null):
    health -= amount
    health = max(0, health)
    emit_signal("health_changed", health)

Use uma linha vazia dentro de funções para separar seções lógicas.

Tamanho de linha

Mantenha linhas individuais de código abaixo de 100 caracteres.

Se puder, tente manter linhas com menos de 80 caracteres. Isso ajuda a ler o código em telas pequenas e com dois scripts abertos lado-a-lado em um editor de texto externo. Por exemplo, quando estiver olhando uma revisão diferente.

Uma declaração por linha

Nunca combine várias instruções em uma única linha. Não, programadores de C, nem com uma expressão condicional de uma linha só.

Bom:

if position.x > width:
    position.x = 0

if flag:
    print("flagged")

Ruim:

if position.x > width: position.x = 0

if flag: print("flagged")

A única exceção a essa regra é o operador ternário:

next_state = "fall" if not is_on_floor() else "idle"

Evite parênteses desnecessários

Evite parênteses em instruções e expressões condicionais. A não ser que eles sejam necessários para garantir a ordem das operações, eles somente reduzem a legibilidade.

Bom:

if is_colliding():
    queue_free()

Ruim:

if (is_colliding()):
    queue_free()

Operadores booleanos

Prefira a versão por extenso dos operadores booleanos, já que elas são mais acessíveis:

  • Use and ao invés de &&.

  • Use or ao invés de ||.

Você pode usar parênteses ao redor de operadores booleanos para tirar ambiguidade. Isso pode facilitar e leitura de expressões longas.

Bom:

if (foo and bar) or baz:
    print("condition is true")

Ruim:

if foo && bar || baz:
    print("condition is true")

Espaçamento de comentários

Comentários normais devem começar com um espaço, ao contrário de código que você desativa usando um comentário. Isso ajuda a diferenciar comentários em texto de código desativado.

Bom:

# This is a comment.
#print("This is disabled code")

Ruim:

#This is a comment.
# print("This is disabled code")

Nota

No editor de script, para alternar o código selecionado comentado, aperte Ctrl + K. Este recurso adiciona um sinal de # no início das linhas selecionadas.

Espaço em branco

Sempre use um espaço ao redor de operadores e depois de vírgulas. Evite espaços extras em referências de dicionário e chamadas de função.

Bom:

position.x = 5
position.y = target_position.y + 10
dict["key"] = 5
my_array = [4, 5, 6]
print("foo")

Ruim:

position.x=5
position.y = mpos.y+10
dict ["key"] = 5
myarray = [4,5,6]
print ("foo")

Não use espaços para alinhas expressões verticalmente:

x        = 100
y        = 100
velocity = 500

Aspas

Use duas aspas a não ser que uma aspa só te deixe escapar menos caracteres em uma string. Veja os exemplos abaixo:

# Normal string.
print("hello world")

# Use double quotes as usual to avoid escapes.
print("hello 'world'")

# Use single quotes as an exception to the rule to avoid escapes.
print('hello "world"')

# Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
print("'hello' \"world\"")

Números

Não omita o zero à esquerda ou à direita em números de ponto flutuante. Caso contrário, isto os torna menos legíveis e mais difíceis de distinguir de inteiros à primeira vista.

Bom:

var float_number = 0.234
var other_float_number = 13.0

Ruim:

var float_number = .234
var other_float_number = 13.

Use letras minúsculas em números hexadecimais, pois sua altura mais baixa torna o número mais fácil de ler.

Bom:

var hex_number = 0xfb8c0b

Ruim:

var hex_number = 0xFB8C0B

Aproveite os sublinhados do GDScript em literais para tornar números grandes mais legíveis.

Bom:

var large_number = 1_234_567_890
var large_hex_number = 0xffff_f8f8_0000
var large_bin_number = 0b1101_0010_1010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12345

Ruim:

var large_number = 1234567890
var large_hex_number = 0xfffff8f80000
var large_bin_number = 0b110100101010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12_345

Convenções de nomes

Estas convenções de nomeação seguem o estilo do Godot Engine. Quebrá-las fará com que o seu código fique diferente das convenções de nomeação embutidas, o que deixa o seu código inconsistente.

Nomes de arquivo

Use snake_case para nomes de arquivo. Para classes nomeadas, converta o nome da classe PascalCase em snake_case:

# This file should be saved as `weapon.gd`.
class_name Weapon
extends Node
# This file should be saved as `yaml_parser.gd`.
class_name YAMLParser
extends Object

Isto é consistente com a forma como os arquivos C++ são nomeados no código-fonte do Godot. Isto também evita problemas de sensibilidade de caixa que podem surgir ao exportar um projeto do Windows para outras plataformas.

Classes e nós

Use PascalCase para nomes de classes e nós:

extends KinematicBody

Use PascalCase também quando estiver carregando uma classe em uma constante ou variável:

const Weapon = preload("res://weapon.gd")

Funções e variáveis

Use snake_case para nomear funções e variáveis:

var particle_effect
func load_level():

Prefixe métodos virtuais (funções que o usuário deve sobrescrever), funções privadas e variáveis privadas com um único sublinhado (_):

var _counter = 0
func _recalculate_path():

Sinais

Utilize o tempo pretérito para nomear sinais:

signal door_opened
signal score_changed

Constantes e enums

Utilize CONSTANT_CASE, com todas as letras maiúsculas e um sublinhado para separas as palavras:

const MAX_SPEED = 200

Use PascalCase para nomes de enums e CONSTANT_CASE para seus membros, já que são constantes:

enum Element {
    EARTH,
    WATER,
    AIR,
    FIRE,
}

Ordem do código

Esta primeira seção foca na ordem do código. Para formatação, veja formatação. Para convenções de nomes, veja convenções de nomes.

Sugerimos que você organize seu código GDScript assim:

01. tool
02. class_name
03. extends
04. # docstring

05. signals
06. enums
07. constants
08. exported variables
09. public variables
10. private variables
11. onready variables

12. optional built-in virtual _init method
13. built-in virtual _ready method
14. remaining built-in virtual methods
15. public methods
16. private methods

Otimizamos essa ordem pra deixar o código mais fácil de ler de cima pra baixo, para ajudar desenvolvedores lendo o código pela primeira vez a entender como ele funciona, e para evitar erros referentes à ordem da declaração de variáveis.

Essa ordem de código segue quatro regras gerais:

  1. Propriedades e sinais vêm primeiro, seguidos por métodos.

  2. Público vem antes de privado.

  3. Callbacks virtuais vêm antes da interface da classe.

  4. As funções de construção e inicialização do objeto, _init e _ready, vêm antes de funções que o modificam durante a execução.

Declaração de classe

Se o código deve rodar no editor, coloque a palavra-chave tool na primeira linha do script.

Siga com um class_name se necessário. Você pode tornar um arquivo GDScript em um tipo global no seu projeto usando este recurso. Para mais informações, veja Básicos do GDScript.

Então, adicione a palavra-chave extends se a classe extende de um tipo embutido.

Seguido disso, você pode ter a docstring opcional da classe como comentários. Você pode usar isso para explicar o papel de sua classe a seus colegas de equipe, como ela funciona, e como outros desenvolvedores devem usá-la, por exemplo.

class_name MyNode
extends Node
# A brief description of the class's role and functionality.
# Longer description.

Sinais e propriedades

Escreva declarações de sinal, seguida de propriedades, ou seja, variáveis do membro, depois da docstring.

Enums vêm depois de sinais, já que você pode usá-los como inferências de exportação para outras propriedades.

Então, escreva constantes, variáveis exportados, públicos, privados, e onready, nessa ordem.

signal spawn_player(position)

enum Jobs {KNIGHT, WIZARD, ROGUE, HEALER, SHAMAN}

const MAX_LIVES = 3

export(Jobs) var job = Jobs.KNIGHT
export var max_health = 50
export var attack = 5

var health = max_health setget set_health

var _speed = 300.0

onready var sword = get_node("Sword")
onready var gun = get_node("Gun")

Nota

O compilador de GDScript avalia variáveis onready logo após o callback _ready. Você pode usar isso para garantir que todas as dependências existem, ou seja, criar os nós filhos da cena em que sua classe depende. É isso que o exemplo acima mostra.

Variáveis de membro

Não declare variáveis de membro se elas forem usadas apenas localmente em um método, pois torna o código mais difícil de seguir. Em vez disso, declare-as como variáveis locais no corpo do método.

Variáveis locais

Declare variáveis locais o mais pŕoximo possível de seu primeiro uso. Isto torna mais fácil seguir o código, sem ter que rolar muito para encontrar onde a variável foi declarada.

Métodos e funções estáticas

Depois das propriedades da classe vêm os métodos.

Comece com o método _init(), que a engine chamará assim que o objeto for criado na memória. Siga com o método _ready(), que o Godot chama quando ela adiciona um nó à árvore da cena.

Estas funções devem vir primeiro pois elas mostram como o objeto é inicializado.

Outros callbacks virtuais, como _unhandled_input() e _physics_process, devem vir depois. Estes controlam os loops principais do objeto e suas interações com a engine.

O resto da interface da classe, métodos públicos e privados, vêm depois, nessa ordem.

func _init():
    add_to_group("state_machine")


func _ready():
    connect("state_changed", self, "_on_state_changed")
    _state.enter()


func _unhandled_input(event):
    _state.unhandled_input(event)


func transition_to(target_state_path, msg={}):
    if not has_node(target_state_path):
        return

    var target_state = get_node(target_state_path)
    assert(target_state.is_composite == false)

    _state.exit()
    self._state = target_state
    _state.enter(msg)
    Events.emit_signal("player_state_changed", _state.name)


func _on_state_changed(previous, new):
    print("state changed")
    emit_signal("state_changed")

Tipagem estática

Desde o Godot 3.1, GDScript suporta tipagem estática opcional.

Tipos declarados

Para declarar o tipo de uma variável, use <variável>: <tipo>:

var health: int = 0

Para declarar o tipo de retorno de uma função, use -> <type>:

func heal(amount: int) -> void:

Tipos inferidos

Na maioria dos casos, você pode deixar o compilador deduzir o tipo ao usar :=:

var health := 0  # The compiler will use the int type.

No entanto, em alguns casos em que falta contexto, o compilador volta ao tipo de retorno da função. Por exemplo, get_node() não pode inferir um tipo a menos que a cena ou arquivo do nó seja carregado na memória. Nesse caso, você deve definir o tipo explicitamente.

Bom:

onready var health_bar: ProgressBar = get_node("UI/LifeBar")

Ruim:

# The compiler can't infer the exact type and will use Node
# instead of ProgressBar.
onready var health_bar := get_node("UI/LifeBar")