Arma: Les boucles !

Une boucle est un bout de code qui va se répéter encore et encore. Tu vas rapidement en avoir besoin pour faire des choses monstrueuses.

Je te donne un exemple. Imaginons que tu ai besoin de 3 objets identiques dans ton inventaire. Tu devras donc les ajouter 3 fois de suite avec tes petites mimines. Mais qu’est ce qui va se passer quand tu vas en avoir 20, 100 ? A ce moment là, tu vas pouvoir faire appel aux boucles qui feront le travail pour toi !

Il est possible d’utiliser plusieurs types de boucle, et façon de faire.

La boucle for
Cette boucle permet d’éxecuter une portion de code qui se trouve entre des crochets {} un certains nombres de fois. La syntaxe permet de définir le compteur de deux façons différentes. Il y a cependant, en fonction de la syntaxe utilisée, une petite notion de performance.

La boucle la plus rapide est la suivante:

for "_i" from 5 to 93 step 7 do {hint str _i}; 
//affiche 96

La syntaxe est simple à lire, on demande au moteur d’initialiser une variable _i à 5 et de l’incrémenter par pallier(step) de 7 jusqu’à ce que la valeur de _i soit égale ou supérieur à 93. Dès que c’est le cas, la boucle se termine.Sinon, elle affiche la valeur de la variable _i.

Si le pallier n’est pas précisé, par défaut, la variable s’incrémente de 1. Note que dans cet exemple, la valeur de _i va dépasser 93. Ce qui signifie que l’expression n’est réellement évaluée qu’une seul et unique fois.

Tu peux donc utiliser par exemple ceil (random 10) pour générer un nombre aléatoire d’itérations, cela ne sera compilé qu’une seule fois.

Une autre particularité de cette expression est que la variable _i n’a d’existence que dans le scope de la boucle. D’autres variables _i peuvent exister à l’extérieur de la boucle, elles n’affecteront pas le contenu de cette variable. Tu peux créer une boucle infinie en utilisant 0 comme pallier mais c’est fortement déconseillé.

for "_i" from 0 to 1 step 0 do {}; 
// boucle infinie

La façon la plus lente d’utiliser la boucle for est la suivante (environ 2,5 fois plus lente que le cas au dessus)

for [{_i=5},{_i<=93},{_i=_i+7}] do {hint str _i};
//affiche 89

La syntaxe est simple à lire, on demande au moteur d’initialiser une variable _i 5 et de l’incrémenter par pallier(step) de 7 tant que la valeur de _i est égale ou inférieur à 93. Dès que ça n’est plus le cas, la boucle se termine.Sinon, elle affiche la valeur de la variable _i.

Contrairement au 1er exemple, la variable _i ne dépasse jamais la limite de 93. La variable _i n’est pas privée sauf si tu la déclares explicitement comme cela.

_i = 101;
for [{private "_i"; _i=5},{_i<=93},{_i=_i+7}] do {};
hint str _i; 
//hint is 101

Cette boucle réévalue à chaque itération que la condition est remplie. C’est la raison pour laquelle elle est lente. Tu peux même modifier dynamiquement le nombre d’itérations maximun pendant qu’elle tourne.

k = 3; a = {k = k + 0.5; k};
for [{_i=0},{_i<(call a)},{_i=_i+1}] do {
     diag_log format ["_i:%1/k:%2", _i, k];
};
//.rpt file
//"_i:0/k:3.5"
//"_i:1/k:4"
//"_i:2/k:4.5"
//"_i:3/k:5"
//"_i:4/k:5.5"
//"_i:5/k:6"
//"_i:6/k:6.5"

Cette boucle peut tourner à l’infinie intentionnellement ou par erreur. Tout ce que tu as à faire est que la condition retourne vrai tout le temps.

for [{_i=0},{true},{_i=_i+1}] do {}; 
// Tourne à l'infini

Tu dois maintenant te demander pourquoi ne pas utiliser une boucle while pour faire pareil.

La boucle while

_i=5; while {_i<=93} do {hint str _i; _i=_i+7}; 
//hint is 89

_i=5; while {_i<=93} do {_i=_i+7}; hint str _i; 
//hint is 96

En fait, on peut avoir les mêmes résultats que ci-dessus en utilisant une boucle while. De la même façon, il est possible de créer un train fou. C’est la raison pour laquelle le moteur limite le nombre d’itérations à 10000 dans les environnements non schédulé et quitte les boucles dès que le compteur des 10000 itérations atteint.

Tu peux vérifier via le débogueur, en exécutant ce code:

i=0; while {true} do {i=i+1}; hint str i; 
//affiche 10000

La limite est fixée quand le script est exécuté par le moteur, et que celui doit attendre le retour du call avant de faire autre chose. Cela peut arriver par exemple, quand tu appelles une boucle infinie dans un event handler, un fichier FSM.

Si ton script est executé en utilisant la commande spawn ou execVM, tu es en environnement schédulé, et ta boucle n’a plus de limites en terme d’itérations. Elle peut tourner à l’infinie.

En résumé:

spawn {while {true} do {}} // boucle à l'infini
call {while {true} do {}} // 10000 boucles max FSM/Event Handler/Editor init field
call {while {true} do {}} // boucle à l'infi si appellé via l'init.sqf ou un script execVMed 
spawn {call {while {true} do {}}} // boucle à l'infini

Beaucoup de personnes personnes utilisent les boucles while avec une condition true. Killzone Kid recommande d’utiliser à la place des handlers quand c’est possible qui déclenchent l’exécution du code quand un évènement survient.

Par exemple, pour vérifier quand un joueur entre dans un véhicule, plutôt que d’utiliser un while true, il est préférable d’utiliser un addEventHandler.

En théorie oui, néanmoins, les gains de performance dépendent clairement de l’implémentation des handlers dans le moteur natif d’Arma et ne sont pas clairement prouvés.

// bien
_veh addEventHandler ["GetIn",{_this call isIn}];
_veh addEventHandler ["GetOut",{_this call isOut}];

// moins bien
lastVehicle = objNull;
lastSeat = "";
null = [] spawn {
    private ["_veh","_roles","_seat"];
    while {true} do {
        _veh = vehicle player;
        if (_veh != player) then {
            _roles = assignedVehicleRole player;
            _seat = if (count _roles > 0) then [{_roles select 0},{""}];
            if (_veh != lastVehicle || _seat != lastSeat) then {
                lastVehicle = _veh;
                lastSeat = _seat;
                [_veh, _seat, player] call isIn;
            };
        } else {
            if (!isNull lastVehicle) then {
                [lastVehicle, lastSeat, player] call isOut;
            };
        };
    sleep 0.1;
    };
};

Si tui veux des scripts qui s’exécutent avec plus de fluidité, je te recommande d’utiliser des sleeps dans tes boucles. Le sleep permet de restituer du temps à l’ordonnanceur, qui lui même partage le temps entre les différents scripts pour qu’ils puissent s’exécuter. Les event handler du fait de leur implémentation sont exécutés dès que possible à partir du moment ou ils sont déclenchés.

Cet article a été initialement rédigé par Killzone Kid, réactualisé 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