Scripting (suite)

Traitement

Plusieurs actions dans Godot sont déclenchées par des callbacks ou des fonctions virtuelles, il n’y a donc pas besoin d’écrire du code qui s’exécute en permanence.

Cependant, il est fréquent d’avoir besoin qu’un script soit exécuté à chaque rafraichissement. Il y a deux types de traitements : les traitements passifs et les traitements physiques.

Le traitement passif est activé lorsque la méthode Node._process() se trouve dans un script. Il peut être activé ou désactivé avec la fonction Node.set_process().

Cette méthode sera appelée à chaque fois qu’une image est dessinée, elle dépend donc entièrement du nombre d’images par seconde (IPS/FPS) auquel l’application s’exécute :

func _process(delta):
    # Do something...
    pass
public override void _Process(float delta)
{
    // Do something...
}

Le paramètre delta contient le temps écoulé en secondes, en virgule flottante, depuis l’appel précédent à _process().

Ce paramètre peut être utilisé pour garantir que les actions prendront le même temps, quelque soit le nombre d’images par secondes du jeu.

Par exemple, le mouvement est souvent multiplié par un delta-temps pour que la vitesse de mouvement soit constante et indépendante de la fréquence de rafraichissement.

Le traitement physique avec _physics_process() est similaire, mais devrait être utilisé pour des traitements qui doivent se produire avant chaque étape physique, comme lorsqu’on contrôle un personnage. Il est toujours exécuté avant une étape physique et est appelé à intervalles fixes : 60 fois par secondes par défaut. Vous pouvez changer l’intervalle dans les paramètres du projet sous Physics -> Common -> Physics Fps.

La fonction _process(), cependant, n’est pas synchronisée avec la physique. Son taux de rafraîchissement n’est pas constant et dépend de l’optimisation du hardware et du jeu. Son exécution se fait après le traitement de la physique sur les jeux mono-threadés.

Une façon simple de tester ceci est de créer une scène avec un seul nœud Label, avec le script suivant :

extends Label

var accum = 0

func _process(delta):
    accum += delta
    text = str(accum) # 'text' is a built-in label property.
public class CustomLabel : Label
{
    private float _accum;

    public override void _Process(float delta)
    {
        _accum += delta;
        Text = _accum.ToString(); // 'Text' is a built-in label property.
    }
}

Ce qui montrera un compteur s’incrémentant à chaque image.

Groupes

Les nœuds peuvent être ajoutés à des groupes, autant que souhaité par nœud, et c’est une fonctionnalité utile pour organiser de grandes scènes. Il y a deux façons de le faire. La première provient de l’interface utilisateur, du bouton Groupes sous le panneau Nœud :

../../_images/groups_in_nodes.png

Et la deuxième façon est par le code. Un exemple serait de marquer les nœuds qui sont des ennemis :

func _ready():
    add_to_group("enemies")
public override void _Ready()
{
    base._Ready();

    AddToGroup("enemies");
}

De cette façon, si le joueur est découvert en s’infiltrant dans une base secrète, tous les ennemis peuvent être avertis de l’alarme qu’il déclenche en utilisant SceneTree.call_group() :

func _on_discovered(): # This is a purely illustrative function.
    get_tree().call_group("enemies", "player_was_discovered")
public void _OnDiscovered() // This is a purely illustrative function.
{
    GetTree().CallGroup("enemies", "player_was_discovered");
}

Le code ci-dessus appelle la fonction player_was_discovered sur chaque membre du groupe ennemies.

Il est également possible d’obtenir la liste complète des nœuds ennemies en appelant SceneTree.get_nodes_in_group() :

var enemies = get_tree().get_nodes_in_group("enemies")
var enemies = GetTree().GetNodesInGroup("enemies");

La classe SceneTree fournit de nombreuses méthodes utiles, comme l’interaction avec les scènes, leur hiérarchie de nœuds et leur groupes de nœuds. Elle vous permet de changer facilement de scènes ou de les recharger, de quitter le jeu ou de le dé-pauser. Elle contient même des signaux intéressants. Alors allez voir si vous avez le temps !

Notifications

Godot possède un système de notifications. Celles-ci ne sont généralement pas nécessaires pour le scripting, car elles sont de trop bas niveau et des fonctions virtuelles sont fournies pour la plupart d’entre elles. C’est cependant bien de savoir qu’elles existent. Par exemple, vous pouvez ajouter une fonction Object.Notification() dans votre script :

func _notification(what):
    match what:
        NOTIFICATION_READY:
            print("This is the same as overriding _ready()...")
        NOTIFICATION_PROCESS:
            print("This is the same as overriding _process()...")
public override void _Notification(int what)
{
    base._Notification(what);

    switch (what)
    {
        case NotificationReady:
            GD.Print("This is the same as overriding _Ready()...");
            break;
        case NotificationProcess:
            var delta = GetProcessDeltaTime();
            GD.Print("This is the same as overriding _Process()...");
            break;
    }
}

La documentation de chaque classe dans la Class Reference montre les notifications qu’elle peut recevoir. Cependant, dans la plupart des cas, GDScript fournit des fonctions de surcharge plus simples.

Fonctions surchargeables

De telles fonctions surchargeables, décrites comme suit, peuvent être appliquées aux nœuds :

func _enter_tree():
    # When the node enters the _Scene Tree_, it becomes active
    # and  this function is called. Children nodes have not entered
    # the active scene yet. In general, it's better to use _ready()
    # for most cases.
    pass

func _ready():
    # This function is called after _enter_tree, but it ensures
    # that all children nodes have also entered the _Scene Tree_,
    # and became active.
    pass

func _exit_tree():
    # When the node exits the _Scene Tree_, this function is called.
    # Children nodes have all exited the _Scene Tree_ at this point
    # and all became inactive.
    pass

func _process(delta):
    # This function is called every frame.
    pass

func _physics_process(delta):
    # This is called every physics frame.
    pass
public override void _EnterTree()
{
    // When the node enters the _Scene Tree_, it becomes active
    // and  this function is called. Children nodes have not entered
    // the active scene yet. In general, it's better to use _ready()
    // for most cases.
    base._EnterTree();
}

public override void _Ready()
{
    // This function is called after _enter_tree, but it ensures
    // that all children nodes have also entered the _Scene Tree_,
    // and became active.
    base._Ready();
}

public override void _ExitTree()
{
    // When the node exits the _Scene Tree_, this function is called.
    // Children nodes have all exited the _Scene Tree_ at this point
    // and all became inactive.
    base._ExitTree();
}

public override void _Process(float delta)
{
    // This function is called every frame.
    base._Process(delta);
}

public override void _PhysicsProcess(float delta)
{
    // This is called every physics frame.
    base._PhysicsProcess(delta);
}

Comme mentionné précédemment, il est préférable d’utiliser ces fonctions plutôt que le système de notification.

Création de nœuds

Pour créer un nœud à partir du code, appelez la méthode .new(), comme pour tout autre type de données de classe. Par exemple :

var s
func _ready():
    s = Sprite.new() # Create a new sprite!
    add_child(s) # Add it as a child of this node.
private Sprite _sprite;

public override void _Ready()
{
    base._Ready();

    _sprite = new Sprite(); // Create a new sprite!
    AddChild(_sprite); // Add it as a child of this node.
}

Pour supprimer un nœud, que ce soit à l’intérieur ou à l’extérieur de la scène, il faut utiliser free() :

func _someaction():
    s.free() # Immediately removes the node from the scene and frees it.
public void _SomeAction()
{
    _sprite.Free(); // Immediately removes the node from the scene and frees it.
}

Lorsqu’un nœud est libéré, il libère également tous ses nœuds enfants. Pour cette raison, la suppression manuelle des nœuds est beaucoup plus simple qu’il n’y paraît. Libérez le nœud de base et tout le reste dans le sous-arbre disparaît avec lui.

Une situation peut se produire dans laquelle nous voulons supprimer un nœud qui est actuellement « bloqué », parce qu’il émet un signal ou appelle une fonction. Ceci fera planter le jeu. Lancer Godot avec le débogueur permet souvent de détecter ce cas et de vous en avertir.

La façon la plus sûre de supprimer un nœud est d’utiliser Node.queue_free(). Ceci efface le nœud en toute sécurité pendant la période d’inactivité.

func _someaction():
    s.queue_free() # Removes the node from the scene and frees it when it becomes safe to do so.
public void _SomeAction()
{
    _sprite.QueueFree(); // Removes the node from the scene and frees it when it becomes safe to do so.
}

Instancier des scènes

Instancier une scène depuis le code se fait en deux étapes. La première est de charger la scène à partir de votre disque dur :

var scene = load("res://myscene.tscn") # Will load when the script is instanced.
var scene = GD.Load<PackedScene>("res://myscene.tscn"); // Will load when the script is instanced.

Le préchargement peut être plus pratique, car il se produit lors de l’analyse du code (GDScript uniquement) :

var scene = preload("res://myscene.tscn") # Will load when parsing the script.

Mais scene n’est pas encore un nœud. Elle est emballée dans une ressource spéciale appelée PackedScene. Pour créer le nœud, la fonction PackedScene.instance() doit être appelée. Cela retournera l’arborescence des nœuds qui peuvent être ajoutés à la scène active :

var node = scene.instance()
add_child(node)
var node = scene.Instance();
AddChild(node);

L’avantage de ce processus en deux temps est qu’une scène emballée peut être chargée et prête à l’emploi afin que vous puissiez créer autant d’instances que vous le souhaitez. C’est particulièrement utile pour instancier rapidement plusieurs ennemis, balles et autres entités dans la scène active.

Enregistrer des scripts en tant que classes

Godot fournit une fonctionnalité « Script Class », permettant d’enregistrer des scripts individuels avec l’éditeur. Par défaut, l’accès aux scripts anonymes n’est possible qu’en chargeant le fichier directement.

Vous pouvez nommer un script et l’enregistrer comme type dans l’éditeur avec le mot-clé class_name suivi par le nom de la classe. Vous pouvez ajouter une virgule et un éventuel chemin vers une image pour l’utiliser en tant qu’icône. Vous trouverez alors votre nouveau type dans la boîte de dialogue de création de nœuds ou de ressources.

extends Node

# Declare the class name here
class_name ScriptName, "res://path/to/optional/icon.svg"

func _ready():
    var this = ScriptName           # reference to the script
    var cppNode = MyCppNode.new()   # new instance of a class named MyCppNode

    cppNode.queue_free()
../../_images/script_class_nativescript_example.png

Avertissement

Dans Godot v3.11 :

  • Seuls GDScript et NativeScript, c’est-à-dire C ++ et d’autres langages alimentés par GDNative, peuvent enregistrer des scripts.
  • Seul GDScript crée des variables globales pour chaque script nommé.