Quand utiliser des scènes ou des scripts

Nous avons déjà expliqué en quoi les scènes et les scénarios sont différents. Les scripts définissent une extension de classe du moteur avec code impératif, les scènes avec code déclaratif.

Les capacités de chaque système sont donc différentes. Les scènes peuvent définir comment une classe étendue s'initialise, mais pas quel est son comportement réel. Les scènes sont souvent utilisées en conjonction avec un script de sorte que la scène agit comme une extension du code déclaratif du script.

Types anonymes

Il est possible de définir complètement le contenu des scènes à l’aide d’un seul script. Il s’agit, en substance, ce que fait l’éditeur Godot, uniquement dans le constructeur de C++ de ses objets.

Mais, choisir lequel utiliser peut être un dilemme. La création d'instances de script est identique à la création de classes dans le moteur alors que la gestion des scènes nécessite un changement dans l'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.instance() # Different method call
var my_inherited_scene = MyScene.instance(PackedScene.GEN_EDIT_STATE_MAIN) # Create scene inheriting from MyScene

Aussi, les scripts fonctionnent un peu plus lentement que les scènes en raison de la différence de vitesse entre le moteur et le code de script. Plus grand et complexe est le nœud, plus il y a de raison de le construire comme une scène.

Types nommés

Dans certains cas, un utilisateur peut enregistrer un script comme un nouveau type dans l'éditeur lui-même. Il s'affiche sous la forme d'un nouveau type dans la boîte de dialogue de création de nœud ou de ressource avec une icône facultative. Dans ces cas, la capacité de l'utilisateur à utiliser le script est beaucoup plus simple. Plutôt que d'avoir à...

  1. Connaître le type de base du script qu'ils aimeraient utiliser.

  2. Créez une instance de ce type de base.

  3. Ajoutez le script au nœud.

    1. (Méthode glisser-déposer)

      1. Recherchez le script dans le dock de système de fichiers.

      2. Glissez et déposez le script sur le nœud dans le dock de la scène.

    2. (Méthode de la propriété)

      1. Faites défiler vers le bas de l'inspecteur pour trouver la propriété script et sélectionnez-la.

      2. Sélectionnez "Charger" dans la liste déroulante.

      3. Sélectionnez le script dans la boîte de dialogue fichier.

Avec un script enregistré, le type de script devient une option de création comme les autres nœuds et ressources du système. Il n'est pas nécessaire d'effectuer les travaux ci-dessus. La boite de dialogue de création a même une barre de recherche pour rechercher le type par nom.

Il y a deux systèmes d’enregistrement des types...

  • Types personnalisés

    • Éditeur seulement. Les noms de types ne sont pas accessibles au moment de l'exécution.

    • Ne prend pas en charge les types personnalisés hérités.

    • Un outil d'initialisation. Crée le nœud avec le script. Rien de plus.

    • L'éditeur n'est pas conscient du type du script ou de sa relation avec d'autres types du moteurs ou scripts.

    • Permet aux utilisateurs de définir une icône.

    • Fonctionne pour tous les langages de script parce qu'il traite des ressources de script dans l'abstrait.

    • Configurer avec EditorPlugin.add_custom_type.

  • Classes Script

    • Editeur et runtime accessibles.

    • Affiche l'intégralité des relations d'héritage.

    • Crée le nœud avec le script, mais peut aussi changer de type ou étendre le type depuis l'éditeur.

    • L'éditeur est conscient des relations d'héritage entre les scripts, les classes de script et les classes C++ du moteur.

    • Permet aux utilisateurs de définir une icône.

    • Les développeurs du moteur doivent ajouter le support pour les langues manuellement (à la fois le nom de l'exposition et l'exécution de l'accessibilité).

    • Godot 3.1+ uniquement.

    • L'éditeur analyse les dossiers de projet et enregistre tous les noms exposés pour tous les langages de script. Chaque langage de script doit implémenter son propre support pour exposer ces informations.

Les deux méthodologies ajoutent des noms au dialogue de création, mais les classes de script, en particulier, permettent également aux utilisateurs d'accéder au nom de type sans charger la ressource script. Créer des instances et accéder à des constantes ou à des méthodes statiques est viable de n'importe où.

Avec de telles fonctionnalités, on peut souhaiter que leur type soit un script sans scène en raison de la facilité d'utilisation qu'il offre aux utilisateurs. Ceux qui développent des plugins ou qui créent des outils internes pour les concepteurs trouveront les choses plus faciles de cette façon.

Le revers de la médaille, est que cela signifie aussi avoir à utiliser largement la programmation impérative.

Performances de Script vs PackedScene

Un dernier aspect à considérer lors du choix des scènes et des scripts est la vitesse d'exécution.

Au fur et à mesure que la taille des objets augmente, la taille des scripts nécessaire pour les créer et les initialiser s'agrandit beaucoup. La création de hiérarchies de nœuds en est la preuve. La logique de chaque nœud peut comporter plusieurs centaines de lignes de code.

L'exemple de code ci-dessous crée un nouveau Node, change son nom, lui attribue un script, définit son futur parent comme propriétaire afin qu'il soit enregistré sur le disque en même temps que lui, et enfin l'ajoute comme enfant du noeud Main :

# Main.gd
extends Node

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

Un code de script comme celui-ci est beaucoup plus lent que le code C ++ côté moteur. Chaque instruction fait un appel à l'API de script qui conduit à de nombreuses "recherches" sur le back-end pour trouver la logique à exécuter.

Les scènes aident à éviter ce problème de performances. PackedScene, le type de base dont les scènes héritent, définit les ressources qui utilisent des données sérialisées pour créer des objets. Le moteur peut traiter les scènes par lots sur le back-end et offrir de bien meilleures performances que les scripts.

Conclusion

En fin de compte, la meilleure approche consiste à tenir compte de ce qui suit :

  • Si l'on souhaite créer un outil de base qui sera réutilisé dans plusieurs projets différents et que les gens de tous les niveaux de compétence utiliseront probablement (y compris ceux qui ne se qualifient pas de "programmeurs"), alors il y a de fortes chances pour que ce soit un script, probablement avec un nom/un icône personnalisé.

  • Si l'on souhaite créer un concept propre à son jeu, il doit toujours s'agir d'une scène. Les scènes sont plus faciles à suivre/modifier et offrent plus de sécurité que les scripts.

  • Si l'on veut donner un nom à une scène, alors ils peuvent toujours le faire dans 3.1 en déclarant une classe de script et en lui donnant une scène comme une constante. Le script devient, en effet, un espace de noms :

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