Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Quando usar cenas versus scripts

Já abordamos como cenas e scripts são diferentes. Scripts definem uma extensão de classe de mecanismo com código imperativo, cenas com código declarativo.

Each system's capabilities are different as a result. Scenes can define how an extended class initializes, but not what its behavior actually is. Scenes are often used in conjunction with a script, the scene declaring a composition of nodes, and the script adding behaviour with imperative code.

Tipos anônimos

É possível definir completamente o conteúdo de uma cena usando apenas um script. Isto é, em essência, o que o Editor do Godot faz, apenas no construtor em C++ des seus objetos.

Mas, escolher qual deles usar pode ser um dilema. A criação de instâncias de script é idêntica à criação de classes no motor, enquanto o manuseio de cenas requer uma mudança na API:

const MyNode = preload("my_node.gd")
const MyScene = preload("my_scene.tscn")
var node = Node.new()
var my_node = MyNode.new() # Same method call.
var my_scene = MyScene.instantiate() # Different method call.
var my_inherited_scene = MyScene.instantiate(PackedScene.GEN_EDIT_STATE_MAIN) # Create scene inheriting from MyScene.

Além disso, os scripts irão operar um pouco mais lentamente do que as cenas devido às diferenças de velocidade entre o motor e o código do script. Quanto maior e mais complexo o nó, mais razão haverá para construí-lo como uma cena.

Tipos nomeados

Scripts can be registered as a new type within the editor itself. This displays it as a new type in the node or resource creation dialog with an optional icon. This way, the user's ability to use the script is much more streamlined. Rather than having to...

  1. Conhecer o tipo base do script que eles gostariam de usar.

  2. Criar uma instância desse tipo base.

  3. Incluir o script ao nó.

With a registered script, the scripted type instead becomes a creation option like the other nodes and resources in the system. The creation dialog even has a search bar to look up the type by name.

There are two systems for registering types:

  • Tipos Personalizados

    • Apenas editor. Os nomes de tipo não são acessíveis durante a execução.

    • Não suporta tipos personalizados herdados.

    • Uma ferramenta de inicialização. Cria o nó com o script. Nada mais.

    • O editor não reconhece o tipo do script ou seu relacionamento com outros tipos do motor ou scripts.

    • Permite que os usuários definam um ícone.

    • Funciona para todas as linguagens de scripting pois lida com recursos de Script em abstrato.

    • Configurado usando EditorPlugin.add_custom_type.

  • Script Classes

    • Acessíveis pelo editor e pela execução.

    • Exibe relações de herança na íntegra.

    • Cria o nó com o script, mas também pode alterar os tipos ou estender o tipo a partir do editor.

    • O editor está ciente das relações de herança entre scripts, classes de script e classes C++ do motor.

    • Permite que os usuários definam um ícone.

    • Os desenvolvedores do motor devem adicionar suporte a linguagens manualmente (tanto a exposição do nome quanto a acessibilidade durante a execução).

    • Apenas em Godot 3.1+.

    • O Editor verifica pastas do projeto e registra todos os nomes expostos para todas as linguagens de scripting. Cada linguagem de scripting deve implementar seu próprio suporte para expor estas informações.

Ambas as metodologias adicionam nomes à caixa de diálogo de criação, mas as classes de script, em particular, também permitem que os usuários acessem o nome do tipo sem carregar o recurso de script. Criar instâncias e acessar constantes ou métodos estáticos é viável de qualquer lugar.

Com funcionalidades como estas, pode-se desejar que seu tipo seja um script sem uma cena devido à facilidade de uso que ele proporciona aos usuários. Aqueles que desenvolvem plug-ins ou criam ferramentas internas para os designers usarem acharão mais fácil as coisas desta forma.

No lado negativo, também significa ter de utilizar uma programação imperativa.

Desempenho do Script vs PackedScene

Um último aspecto a considerar ao escolher cenas e scripts é a velocidade de execução.

Conforme o tamanho dos objetos aumenta, o tamanho necessário dos scripts para criá-los e inicializá-los fica muito maior. A criação de hierarquias de nós demonstra isso. A lógica de cada Nó pode ter várias centenas de linhas de código.

O exemplo de código abaixo cria um novo Node, muda seu nome, atribui um script a ele, define seu futuro pai como seu proprietário para que seja salvo no disco junto com ele e, finalmente, o adiciona como filho do nó Main:

# main.gd
extends Node

func _init():
    var child = Node.new()
    child.name = "Child"
    child.script = preload("child.gd")
    add_child(child)
    child.owner = self

Um código de script como este é muito mais lento do que o código C++ do lado do motor. Cada instrução faz uma chamada para a API de scripting que leva a muitas "pesquisas" no back-end para encontrar a lógica a ser executada.

Cenas ajudam a evitar este problema de desempenho. PackedScene, o tipo base do qual as cenas herdam, define recursos que utilizam dados serializados para criar objetos. O motor pode processar cenas em lotes no back-end e fornecer um desempenho muito melhor do que os scripts.

Conclusão

No final, a melhor abordagem é considerar o seguinte:

  • Se alguém deseja criar uma ferramenta básica que será reutilizada em vários projetos diferentes e que pessoas de todos os níveis de habilidade provavelmente usarão (incluindo aqueles que não se rotulam como "programadores"), então é provável que deva ser um script, provavelmente um com um nome/ícone personalizado.

  • Se alguém deseja criar um conceito que seja específico para seu jogo, então deve ser sempre uma cena. As cenas são mais fáceis de rastrear/editar e fornecem mais segurança do que os scripts.

  • If one would like to give a name to a scene, then they can still sort of do this by declaring a script class and giving it a scene as a constant. The script becomes, in effect, a namespace:

    # game.gd
    class_name Game # extends RefCounted, so it won't show up in the node creation dialog.
    extends RefCounted
    
    const MyScene = preload("my_scene.tscn")
    
    # main.gd
    extends Node
    func _ready():
        add_child(Game.MyScene.instantiate())