Les fonctions
Qu'est-ce ?
En programmation, il arrive très souvent que l'on répète une tache identique ou semblable à divers endroits de notre code. E.g. calculer le carré d'un nombre ou, autre exemple, dans arma récupérer le joueur le plus proche d'une position (aucune commande arma pour cela). On est donc obligé de construire notre propre algorithme pour le faire. Une fois construit il serait pratique de pouvoir, au lieu de le copier coller partout, l'appeler sous un mot-clef comme une commande. L'avantage est que si on a une erreur dans l'agorithme il n'y a pas besoin de la corriger dans tous les endroits où l'on exécute notre code, seulement à l'endroit où on le définit.
Une telle structure syntaxique porte un nom, c'est lafonction. Son appellation vient tout simplement de l'objet mathématique du même nom.
maSuperbeFonction
peut prendre les paramètres monPremierMagnifiqueParamètre, monDeuxièmeMagnifiqueParamètre, etc
et retourne maBrillantissimeValeur
.
Le principe est donc clair, vous écrivez une seule fois le code de votre fonction en définissant les paramètres attendus ainsi que la valeur retournée. Ensuite il ne vous reste plus qu'appeler là où vous le voulez dans votre code en écrivant maSuperbeFonction(…)
(Dans un language bien écrit, hein Bohémia avec ton SQF).
En SQF
Si vous êtes ici c'est bien parce que vous voulez faire du scripting SQF qui supporte bien heureusement les fonctions. Il vaut mieux pour un langage s'appellant “Status Quo Function”…
En SQF, il y a deux manières de créer une fonction :
- Les fonctions de fichier, on écrit la fonction dans un fichier SQF séparé avant de la compiler pour pouvoir l'appeler dans nos scripts.
- Les fonctions en ligne (inline function), désignant les fonctions que vous créez à l'intérieur de votre fichier script.
Deux points positifs sont :
- La syntaxe pour les paramètres est identique dans les deux cas ainsi que pour la valeur de retour.
- Il suffit de suivre la recette de cuisine pour pas se tromper pour déclarer ces paramètres. (Bon ça c'est commun à tous les langages).
Le point négatif est que le SQF a décidé de ne pas suivre une syntaxe conventionnel (parce que Bohémia) pour les fonctions si l'on compare le SQF à celle des langages de programmation répandus, même ancien. Toutefois pas d'inquiétude, c'est abordable.
Construire une fonction
Création et exécution
Une fonction en SQF c'est finalement comme n'importe qu'elle autre variable qui contiendrait du texte ou un nombre, sauf que cette fois-ci elle contient du code. La question qui se soulève est donc, comment je créer une variable de type code en SQF. La réponse est très simple, il suffit d'utiliser des accolades comme suit monCode = {};
.
Ici je viens de créer une variable monCode
qui contient un code vide (et qui ne fait donc rien).
Comment exécuter la fonction ?
Modifions d'abord le contenu de notre fonction qui est pour l'instant vide avec
monCode = { systemChat "Hello from monCode"; };
Une solution intuitive pour l'exécuter serait d'écrire la ligne suivante
monCode;
Malheureusement, rien ne se passe. En fait ici, vous appelez simplement le contenu de votre variable, i.e. le contenu de votre code mais il n'est pas exécuté. Pour le faire exécuter, il existe une commande (en réalité 2 mais c'est pour un autre tuto) : call
.
call monCode;
Si vous exécutez ces lignes dans la console de débug, vous devez avoir le message dans le chat du jeu.
Paramètres
La puissance des fonctions vient principalement de leur paramétrisation. Prenons l'exemple d'une fonction que l'on appelle carre et qui calcul le carré du chiffre 3.
carre = { systemChat "Le carré de 3 est " + str(3^2); };
Si je l'exécute
call carre;
j'obtient dans le chat : Le carré de 3 est 9
Cette fonction est peu pratique car si on veut calculer le carré d'un autre nombre il nous faut réécrire une autre fonction différente et on pert tout l'avantage des fonctions. Heureusement on peut paramétriser nos fonctions.
Pour passer un paramètre à une fonction on l'ajouter devant l'appel de la fonction. Avec notre exemple ça donne :
3 call carre;
Maintenant comment utilise t'on ce paramètre ? C'est là où on rentre dans les spécifications bizarre du SQF. Le paramètre passé va être contenu dans une variable magique appelé _this
.
On réécrit le code de notre fonction comme ceci
carre = { systemChat format["Le carré de %1 est %2", _this, _this^2]; };
où l'on a remplacé la valeur 3 par _this
. Maintenant on peut utiliser notre fonction pour afficher le carré de n'importe quel nombre.
Comment passe t'on plusieurs paramètres ?
Eh bien en SQF, on ne peut pas mais c'est contournable. Tout d'abord l'astuce est de regrouper tous vos paramètres dans un array
(tableau en français) et c'est ce tableau que l'on va passer comme dans l'exemple ci-dessous.
[param_0, param_1] call multiplication;
et dans le code de notre fonction, on peut récupérer nos paramètres comme on extrait les valeurs d'un tableau.
multiplication = { private _x = _this#0; private _y = _this#1; systemChat format["Le produit de %1 par %2 vaut %3", _x, _y, _x * _y]; };
Toutefois Bohémia a créé une commande spécial pour cette situation et avoir une syntaxe plus compréhensible qui est params
. Cela permet de réécrire notre fonction comme
multiplication = { params['_x', '_y']; systemChat format["Le produit de %1 par %2 vaut %3", _x, _y, _x * _y]; };
Sortie
On aimerait maintenant pouvoir récupérer le résultat de notre fonction. Dans un langage normalement constitué, il existe un mot-clef pour cette opération afin que quelqu'un qui lit le code puisse comprendre précisément quelle valeur est retournée en sortie. Pour le SQF, il n'y a pas de mot-clef et il s'agit de la dernière valeur calculée.
carre = { _this * _this; }; multiplication = { params['_x', '_y']; _x * _y }; carreDeTrois = 3 call carre; monSuperbeMultiple = [4, 6] call multiplication; // Retourne 4*6 donc 24
Pour la fonction carre, la dernière opération est notre paramètre multiplié par lui-même (donc son carré) et pour la fonction multiplication c'est _x*_y
. Le point virgule n'est pas obligatoire et la convention est de ne pas le mettre pour spécifié à l'utilisateur que c'est bien la sortie voulue.
Par comparaison voici dans deux autres langages l'implémentation de ces deux fonctions (avec des commentaires) :
En Python
def square(x): """Compute the square of a number Parameters ---------- x : float Returns ------- float Squared number """ return x**2 def multiply(x, y): """Multiply 2 numbers Parameters ---------- x : float y : float Returns ------- float """ return x * y
En C
/** * @brief Compute the square of a number * * * @param x * @return squared number */ double square(double x) { return x*x; } /** * @brief Multiply 2 numbers * * * @param x * @param y * @return */ double multiply(double x, double y) { return x*y; }
Fonction dans un fichier
Il est possible de définir vos fonctions dans des fichiers séparés avec une fonction par fichier. Dans ce cas précis il ne faut pas utiliser d'accolades, le fichier remplissant leur fonction à la place. En fait tout script SQF peut être considéré comme une fonction non paramétrée (comme notre toute première version de la fonction carre).
Ainsi il suffit de copier coller le contenu de vos accolades dans un fichier. Pour pouvoir l'utiliser ailleurs, il suffit de la compiler à l'aide des deux commandes suivantes preprocessFile et compile comme dans l'exemple suivant
maSuperbeFonction = compile preprocessFile "path_to_my_function\maSuperbeFonction.sqf";