Up to date

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

Cuándo usar escenas y cuándo scripts

Ya hemos cubierto la diferencia entre escenas y scripts. Los scripts definen una extensión de clases del motor con código imperativo, las escenas lo hacen con código declarativo.

Como resultado, las capacidades de cada sistema son diferentes. Las escenas pueden definir cómo se inicializa una clase extendida, pero no cuál es su comportamiento. Las escenas también suelen ser usadas en conjunto con scripts, las escenas declarando una composición de nodos y el script agregando comportamiento con código imperativo.

Tipos anónimos

Es posible definir escenas completamente usando sólo scripts. Esto es en escencia lo que hace el editor de Godot, sólo que lo hace en el constructor C++ de sus objetos.

El dilema puede surgir al tener que elegir cuál usar. Crear instancias de un script es idéntico a crear clases en el motor, por lo que manipular escenas requiere un cambio en el 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.

Además, los scripts funcionan un poco más lentos que las escenas debido a la diferencia de velocidad entre el motor y el código del script. Mientras más grande sea, más complejo es el nodo, lo que nos da una mayor razón para construirlo como escena.

Tipos con nombre

Los scripts se pueden registrar como nuevo tipo en el editor mismo. Esto muestra el tipo nuevo de nodo o recurso en el diálogo de creación con un icono opcional. En esos casos, la habilidad del usuario para usar scripts es mucho más simplificada. En lugar de tener que...

  1. Conocer el tipo base del script que quieran usar.

  2. Crear una instancia de un tipo base.

  3. Agrega el script al nodo.

Con un script registrado, el tipo del script se convierte en una opción de creación como los demás Node y Resource en el sistema. No es necesario hacer nada de lo mostrado anteriormente, el diálogo de creación además tiene una barra de búsqueda donde se puede buscar el tipo por nombre.

Existen dos sistemas para registrar tipos:

  • Tipos Personalizados

    • Solo para el editor. Los tipos con nombre no son accesibles en tiempo de ejecución.

    • No soporta tipos personalizados heredados.

    • Una herramienta inicializadora. Crea el nodo con el script, nada más.

    • El editor no está pendiente del tipo del script o su relación con otros tipos del engine o scripts.

    • Permite al usuario definir un icono.

    • Funciona para todos los lenguajes de scripting porque funciona con los recursos Script de manera abstracta.

    • Se configura usando EditorPlugin.add_custom_type.

  • Script Classes

    • Accesible en el editor y en tiempo de ejecución.

    • Muestra las relaciones de herencia de manera completa.

    • Crea el nodo con el script, pero también cambia el tipo o extiende el tipo desde el editor.

    • El editor está pendiente de la relación de herencia entre scripts, clases de script y clases C++ del motor.

    • Permite al usuario definir un icono.

    • Los desarrolladores que trabajan sobre el motor deben agregar soporta para lenguajes manualmente (tanto la exposición del nombre como la accesibilidad en tiempo de ejecución).

    • Sólo en Godot 3.1+.

    • El editor escanea las carpetas del proyecto y registra cualquier nombre expuesto para todos los lenguajes de scripting. Cada lenguaje de scripting debe incorporar su propio soporte para exponer esta información.

Ambas metodologías agregan nombres al diálogo de creación, pero para clases de script en particular, también permite a los usuarios acceder por el nombre del tipo sin necesidad de cargar el recurso de script. La creación de instancias y acceso a constantes o métodos estáticos es viable desde cualquier parte.

Con características como esta, puede preferirse que un tipo sea un script sin una escena debido a la facilidad de uso que le da a los usuarios. Quienes estén desarrollando plugins o creando herramientas propias para diseñadores, encontrarán mucho más sencillo hacer las cosas de este modo.

Un aspecto negativo de esto es que implica tener que usar mucha programación imperativa.

Rendimiento de Script vs a PackedScene

Un último aspecto a considerar al elegir las escenas y los scripts es la velocidad de ejecución.

A medida que aumenta el tamaño de los objetos, el tamaño necesario de los scripts para crearlos e inicializarlos aumenta mucho. La creación de jerarquías de nodos demuestra esto. La lógica de cada nodo puede tener varios cientos de líneas de código de longitud.

El ejemplo de código a continuación crea un nuevo Nodo, cambia su nombre, le asigna un script, establece su padre futuro como propietario para que se guarde en el disco junto con él, y finalmente lo agrega como hijo del Nodo principal:

# main.gd
extends Node

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

El código de secuencia de comandos como este es mucho más lento que el código C++ del lado del motor. Cada instrucción realiza una llamada a la API de scripting que conduce a muchas "búsquedas" en el back-end para encontrar la lógica a ejecutar.

Las escenas ayudan a evitar este problema de rendimiento. PackedScene, el tipo base del que heredan las escenas, define recursos que usan datos serializados para crear objetos. El motor puede procesar escenas en lotes en el back-end y proporcionar un rendimiento mucho mejor que los scripts.

Conclusión

El mejor enfoque consiste en considerar lo siguiente:

  • Si se desea crear una herramienta básica que será reutilizada en distintos proyectos y donde gente de todo tipo de nivel la usará (incluyendo aquellos que no se llaman a si mismos "programadores"), entonces hay chances de que deba ser un script, probablemente uno con un nombre/icono personalizado.

  • Si se desea crear un concepto que es particular para el juego, entonces debe ser una escena. Las escenas son más fáciles de revisar/editar y proveen más seguridad que los scripts.

  • Si uno desea darle un nombre a una escena, también se puede hacer algo así declarando una clase script y dándole la escena como constante. De este modo el script se vuelve un espacio de nombres:

    # 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())