Arma: Scope – les fondamentaux

Scope
Il est important de comprendre comment fonctionne un scope. Le scope correspond à une bloc de code entre un crochet ouvrant { et un crochet fermant }. Dans le cas d’un script sous forme de fichier, le scope commence au début du fichier et se termine à la fin du fichier. Un scope peut être imbriqué dans un autre scope, on parle alors de scope fils et de scope parent.

A chaque fois, que le moteur commence à exécuter un bloc de code, il crée un scope. Quand le code se termine, le moteur détruit le scope. Le code peut se terminer par avance (via un exitwith), normalement(à la fin d’un call, à la fin de la portion de code {}), ou par erreur.

Un scope est crée implicitement à chaque fois qu’un bloc de code est appelé, y compris quand c’est le même bloc de code qui est appelé plusieurs fois. C’est ce qui fait que des variables privées sont stockées dans des scopes privés.

Par exemple si tu spawn deux fois le même code:

_code = { private "_a"; _a = random 10; };

[] spawn _code;
[] spawn _code;

Le moteur va créer deux scopes indépendants, qui contiennent chacun leurs propres instance de la variable privée _a. Les scopes peuvent être empilé les uns sur les autres. Quand un scope est crée, le moteur l’empile sur le scope courant.

Il est également possible de créer explicitement des scopes en utilisant la commande scopeName.

Les règles

La commande call, les structures de contrôle (if, switch, while, foreach, for, waituntil, try…) créent des scopes pour les blocs de codes qu’elles invoquent, et empilent ces scopes sur le scope courant.

Les commandes spawn et execVM, eventhandlers qui crée des blocs de code, créent des nouveaux scopes isolés qui ne sont pas empilés sur le scope courant. C’est la raisons pour laquelle le code spawné ne peut avoir d’accès aux variables privés du scope qui l’a spawné, hormis si tu lui passes des variables en paramètre à travers la variable spéciale _this. Il n’est pas non plus possible de sortir de ce scope pour revenir au scope parent en utilisant exitWith.

Pour une meilleur compréhension de ce principe de scope, Killezone Kid décrit le fonctionnement d’une maison que l’on doit traverser pour atteindre une pièce.

scopeName "exterieur";
call {
    scopeName "maison";
    call {
        scopeName "cuisine";
    };
    call {
        scopeName "chambre";
    };
};

Pour aller de l’extérieur à la chambre, il faudra entrer dans la maison, puis passer par la cuisine, et accéder à la chambre. Si tu veux sortir de la cuisine par une fenêtre, il faudra utiliser les commandes breakOut ou breakTo.

Les deux commandes nécessitent de connaitre le nom du scope. La commande breakTo quand elle est paramétré pour sortir de son scope courant, finira d’abord d’exécuter l’ensemble des scopes fils avant de sortir.

La commande breakOut sort immédiatement du scope défini et peut également retourner une valeur.

Voici 2 exemples plus parlant pour le fonctionnement de la commande breakTo.

    scopeName "exterieur";
    diag_log "scope exterieur";
    _result = call {
        scopeName "maison";
        diag_log "scope maison";
        breakTo "maison";
        _result = call {
            scopeName "cuisine";
            diag_log "scope cuisine";
        };
        call {
            scopeName "chambre";
            diag_log "scope chambre";
            call {
                scopeName "placard";
                diag_log "scope placard";
            };
        };
        call {
            scopeName "chambre2";
            diag_log "scope chambre2";
        };
    };

// affichera dans le rpt
// scope exterieur
// scope maison
// scope cuisine
// scope chambre
// scope placard
// scope chambre2
    scopeName "exterieur";
    diag_log "scope exterieur";
    _result = call {
        scopeName "maison";
        diag_log "scope maison";
        breakTo "exterieur";
        _result = call {
            scopeName "cuisine";
            diag_log "scope cuisine";
        };
        call {
            scopeName "chambre";
            diag_log "scope chambre";
            call {
                scopeName "placard";
                diag_log "scope placard";
            };
        };
        call {
            scopeName "chambre2";
            diag_log "scope chambre2";
        };
    };

// affichera dans le rpt
// scope exterieur
// scope maison

Le même exemple que le premier maintenant avec la commande breakOut

    scopeName "exterieur";
    diag_log "scope exterieur";
    _result = call {
        scopeName "maison";
        diag_log "scope maison";
        42 breakOut "maison";
        _result = call {
            scopeName "cuisine";
            diag_log "scope cuisine";
        };
        call {
            scopeName "chambre";
            diag_log "scope chambre";
            call {
                scopeName "placard";
                diag_log "scope placard";
            };
        };
        call {
            scopeName "chambre2";
            diag_log "scope chambre2";
        };
    };
    diag_log format ["result %1", _result];

// affichera dans le rpt
// scope exterieur
// scope maison
// scope result 42

Une autre commande utile pour sortir du scope courant est la commande exitWith. Cette commande ne permet de sortir que du scope courant en exécutant au préalable un code dans un scope fils encadré par des {};

scopeName "exterieur";
call {
    scopeName "maison";
    call {
        scopeName "cuisine";
        if true exitWith {};
    };
    call {
        scopeName "chambre";
    };
};

Ici la commande exitWith ne fait que sortir du scope « cuisine ». Le code continue donc de s’exécuter directement au niveau du scope « chambre ».

Cette commande n’a pas l’effet escompté dans certaines boucles spéciales comme les onEachFrame, waitUntil qui sont rappelées automatiquement par le moteur depuis l’extérieur. Une façon de sortir complètement de ces boucles spéciales est d’utiliser ce type de code:

onEachFrame {
    if true exitWith {onEachFrame{}};
};

waitUntil {
    if true exitWith {true};
};

Tu dois également faire attention ou tu déclares tes variables privées, les scopes fils héritent des variables privées des scopes parents (ce qui n’est pas le cas dans le sens inverse).

Il est temps maintenant de mettre tes connaissances à profit. La commande switch est une bonne commande qui permet de conserver un code propre plutôt que de multiplier les structures if. Par contre cette commande est un peu plus lente.

private "_num2str";
_num = 0; 
switch _num do {
    case 0: {_num2str = "0"};
    case 1: {_num2str = "1"};
    case 2: {_num2str = "2"};
    default {_num2str = "N/A"};
};

Le même code, très légèrement plus rapide et aussi propre à adapter en fonction de ton utilisation.

private "_num2str";
_num = 0; 
call {
    if (_num isEqualTo 0) exitWith {_num2str = "0"};
    if (_num isEqualTo 1) exitWith {_num2str = "1"};
    if (_num isEqualTo 2) exitWith {_num2str = "2"};
    _num2str = "N/A";
};

Cet article a été initialement rédigé par Killzone Kid, corrigé par Code34

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google

Vous commentez à l’aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s