Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
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.
Como resultado, as capacidades de cada sistema são diferentes. Cenas podem definir como uma classe estendida é inicializada, mas não qual será seu comportamento. As cenas costumam ser usadas em conjunto com scripts: a cena declara uma composição de nós, e o script adiciona comportamento através de código imperativo.
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.
using Godot;
public partial class Game : Node
{
public static CSharpScript MyNode { get; } =
GD.Load<CSharpScript>("res://Path/To/MyNode.cs");
public static PackedScene MyScene { get; } =
GD.Load<PackedScene>("res://Path/To/MyScene.tscn");
private Node _node;
private Node _myNode;
private Node _myScene;
private Node _myInheritedScene;
public Game()
{
_node = new Node();
_myNode = MyNode.New().As<Node>();
// Different than calling new() or MyNode.New(). Instantiated from a PackedScene.
_myScene = MyScene.Instantiate();
// Create scene inheriting from MyScene.
_myInheritedScene = MyScene.Instantiate(PackedScene.GenEditState.Main);
}
}
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 podem ser registrados como um novo tipo dentro do próprio editor. Isso faz com que apareçam como um novo tipo na janela de criação de nós ou recursos, com um ícone opcional. Dessa forma, o uso do script torna-se muito mais simples para o usuário. Em vez de precisar...
Conhecer o tipo base do script que eles gostariam de usar.
Criar uma instância desse tipo base.
Incluir o script ao nó.
Com um script registrado, o tipo com script passa a ser uma opção de criação como qualquer outro nó ou recurso do sistema. A janela de criação inclusive possui uma barra de pesquisa para localizar o tipo pelo nome.
Existem dois sistemas para registrar tipos:
-
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.
-
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).
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
using Godot;
public partial class Main : Node
{
public Node Child { get; set; }
public Main()
{
Child = new Node();
Child.Name = "Child";
var childID = Child.GetInstanceId();
Child.SetScript(GD.Load<Script>("res://Path/To/Child.cs"));
// SetScript() causes the C# wrapper object to be disposed, so obtain a new
// wrapper for the Child node using its instance ID before proceeding.
Child = (Node)GodotObject.InstanceFromId(childID);
AddChild(Child);
Child.Owner = this;
}
}
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:
If one wishes to create a basic tool that is going to be reused in several different projects and which people of all skill levels will likely use (including those who don't label themselves as "programmers"), then chances are that it should probably be a script, likely one with a custom name/icon.
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.
Se alguém quiser dar um nome a uma cena, ainda pode fazer isso de certa forma declarando uma classe de script e atribuindo uma cena a uma constante. O script torna-se, na prática, um 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())
// Game.cs public partial class Game : RefCounted { public static PackedScene MyScene { get; } = GD.Load<PackedScene>("res://Path/To/MyScene.tscn"); } // Main.cs public partial class Main : Node { public override void _Ready() { AddChild(Game.MyScene.Instantiate()); } }