Dans l’article précédent, nous avons vu comment faire un beau drapeau. Il était possible de changer la texture et c’était déjà bien !
Mais on peut aller beaucoup plus loin, sécuriser un peu pour que le script fonctionne en multijoueur. Gérer une animation pour que ce soit un peu plus sympathique ?
Sécuriser client/serveur
Déjà essayons de comprendre pourquoi ?
Pourtant, la documentation montre bien que les conséquences sont GLOBAL
https://community.bistudio.com/wiki/setFlagTexture

Dans presque tous les cas, quand vous avez ce EG, ça signifie que les effets seront transmis à tous les autres clients et serveur.
Sauf que c’est une vieille méthode et qu’il y a un warning dans la description
NOTE: In MP this command has to be executed where Flag Pole is local. If you add Flag Pole in the editor, it will be local to the server, so executing setFlagTexture on the server will change flag texture on all clients. The command is also persistent and is synchronised for JIP clients.
https://community.bistudio.com/wiki/setFlagTexture
Ce qu’explique la documentation étant qu’il faut exécuter cette commande sur le serveur ou le client à qui appartient cet objet.
Si vous avez posé le drapeau via Eden, il appartiendra au serveur, qui fera tourner les objets. On verra plus tard pour une piste dans le cas contraire.
Il y a pas mal de possibilité, mais ma préférée étant le remoteExec qui permet très simplement de demander à un client, ou le serveur d’exécuter du code… autant dire des possibilités infinies
Il y aurait beaucoup à dire sur cette méthode, je vais vous proposer un format qui fait fonctionner tout ce que vous voulez, mais avec moins d’optimisation.
[
[_target],
{
params ["_target"];
_target setFlagTexture "images\cpc.jpg";
}
] remoteExec ["spawn", 2];
Si vous ne comprenez pas tout, ce n’est pas très grave pour le moment.
Ce qu’il faut comprendre c’est trois choses
[_target],
Il permet depuis de passer en paramètre ce dont on aura besoin.
Le script passé par réseau n’aura pas accès à nos autres variables. On aura cependant accès aux variables de celui qu’on appelle. C’est-à-dire que notre flag_0 du début, on y aura accès, puisqu’il existe autant sur notre client, que côté du serveur.
{
params ["_target"];
_target setFlagTexture "images\cpc.jpg";
}
Le script en lui-même qu’on demande d’exécuter, avec notre _target, qui a été passé en paramètre, mais qu’il faut de nouveau extraire du contexte (_this).
] remoteExec ["spawn", 2];
Les deux précédents, étaient les paramètres de la méthode, mais nous arrivons à la méthode en elle-même.
Laisser tomber pour le moment le SPAWN, retenez juste que ça nous permet d’envoyer n’importe quel code sans contrainte.
Le paramètre « 2 » est aussi important, même si dans notre cas, on pourra l’oublier sans grand impacte. C’est ce paramètre qui force l’exécution PAR le serveur et personne d’autre !
Pour vous montrer à quel point cette méthode peut être compliquée, on aurait le même résultat avec :
[_target, "images\cpc.jpg"] remoteExec ["setFlagTexture", 2];
[_target, "images\cpc.jpg"] remoteExec ["setFlagTexture", _target];
[
_target,
{ _this setFlagTexture "images\cpc.jpg"; }
] remoteExec ["spawn", 2];
[_target, "images\cpc.jpg"] remoteExec ["setFlagTexture", [0, 2] select isMultiplayer];
Il me faudrait un article complet pour expliquer, tout ceci, mais c’était seulement pour vous montrer la complexité ou la profondeur de cette méthode. (Ou alors mon esprit tordu, au choix)
Avec ces améliorations, voilà à quoi ressemble notre script :
this addAction
[
"Hisser le drapeau",
{
params ["_target", "_caller", "_actionId", "_arguments"];
_target setVariable ['cpc_flaged', true, true];
[
[_target],
{
params ["_target"];
_target setFlagTexture "images\cpc.jpg";
}
] remoteExec ["spawn", 2];
},
[],
1.5,
true,
true,
"",
"!(_target getVariable ['cpc_flaged', false])",
3,
false,
"",
""
];
Après c’est bien, on a un truc qui marche bien, sécurisé en multijoueur.
Mais il manque quand même un peu d’animation non ?
Changer le drapeau c’est bien, mais le descendre, pour hisser le bon… hey mais c’était censé être le titre « hisser un drapeau » ? 😉
Animation saccadée, mais animation
Pour comprendre un peu le principe, je vous propose une version très moche, mais qui va montrer le fonctionnement de l’animation.
La mise à jour en question, si vous voulez expérimenter.
this addAction
[
"Hisser le drapeau",
{
params ["_target", "_caller", "_actionId", "_arguments"];
_target setVariable ['cpc_flaged', true, true];
[
[_target],
{
params ["_target"];
_target setflagAnimationPhase 0.5;
sleep 1.5;
_target setflagAnimationPhase 0;
sleep 1.5;
_target setFlagTexture "images\cpc.jpg";
sleep 1.5;
_target setflagAnimationPhase 0.5;
sleep 1.5;
_target setflagAnimationPhase 1;
}
] remoteExec ["spawn", 2];
},
[],
1.5,
true,
true,
"",
"!(_target getVariable ['cpc_flaged', false])",
3,
false,
"",
""
];
La nouvelle partie étant :
_target setflagAnimationPhase 0.5;
sleep 1.5;
_target setflagAnimationPhase 0;
sleep 1.5;
_target setFlagTexture "images\cpc.jpg";
sleep 1.5;
_target setflagAnimationPhase 0.5;
sleep 1.5;
_target setflagAnimationPhase 1;
Le drapeau a une animation dédiée et des commandes pour la manipuler.
Ici on vient forcer l’animation se mettre à 50%… soit milieu du poteau, puis 0 soit tout en bas du poteau.
Les sleep 1.5 demandant simplement d’attendre 1,5sec à chaque fois. Pour avoir le temps de voir le changement.
Ensuite on vient changer la texture, donc le drapeau en lui-même.
Il ne reste plus qu’à rejouer avec l’animation, mais au lieu de diminuer la valeur, on l’augmente pour se diriger vers le haut du poteau
Tada, une animation saccadée et horrible !
On pourrait faire une boucle qui viendrait décrémenter petit à petit notre animation phase, … mais ce serait horrible niveau synchronisation réseau… aussi vous allez voir la version suivante est un poil plus compliqué.
Animation et event handler
C’était une première approche, on va devoir parler d’un gros morceau… les Event Handlers. Vous avez une documentation par ici mais si je vous dis qu’elle ne nous servira à rien, parce que notre cas n’est pas listé ? 😀
Pour faire simple, lorsqu’il se passe quelque chose, par exemple lorsque vous utilisez une arme en tirant. Le jeu aura une liste de scripts liées à votre arme pour cet événement qu’est le tir en appelant tous ces scripts un par un.
Il existe une bonne liste d’évènement, comme vous pouvez le voir dans la documentation.
Mais notre action est une action spécifique, qui s’appelle avec une autre fonction.
Pourquoi ? Quand on va lancer une animation, on ne sait pas quand elle va se terminer, même si on connait la durée de l’animation, elle pourrait changer ou être arrêté en plein milieu via une autre action.
Bref le plus propre étant d’utiliser ces évènements. Forcément c’est une nouvelle méthode
[
object,
"Event Name",
{ code }
] call BIS_fnc_addScriptedEventHandler;
La deuxième commande que nous allons utiliser étant BIS_fnc_animateFlag
[object, phaseToReach, Instant] call BIS_fnc_animateFlag;
Pour tester et comprendre je vous propose ce script et la vidéo du résultat
( J’ai un peu réduit la taille des scripts de addAction, puisqu’on cherche à tester et non à avoir un script sécurisé)
[this, "FlagAnimationDone", {
params ["_flag", "_anim"];
systemChat "Animation terminée !";
}] call BIS_fnc_addScriptedEventHandler;
this addAction [
"Monter le drapeau",
{ [(_this#0), 1, false] call BIS_fnc_animateFlag; }
];
this addAction [
"Descendre le drapeau",
{ [(_this#0), 0, false] call BIS_fnc_animateFlag; }
];
Tout du long j’utilise Instant à false, puisqu’on veut l’animation, on ne veut pas qu’elle soit instantanée !
On peut remarquer que notre event handler, ne s’active que lorsqu’on arrive soit tout en haut, soit tout en bas et pas ailleurs.
Comme on ne compte pas donner de contrôle, mais seulement activer l’action, on va pouvoir enchainer. Notre action va faire descendre le drapeau, et l’event handler le fera remonter avec le bon drapeau.
[this, "FlagAnimationDone", {
params ["_flag", "_anim"];
if(_anim == 0) then {
_flag spawn {
_this setFlagTexture "images\cpc.jpg";
sleep 1;
[_this, 1, false] call BIS_fnc_animateFlag;
};
};
}] call BIS_fnc_addScriptedEventHandler;
this addAction
[
"Hisser le drapeau",
{
params ["_target", "_caller", "_actionId", "_arguments"];
_target setVariable ['cpc_flaged', true, true];
[_target, 0, false] call BIS_fnc_animateFlag;
},
[],
1.5,
true,
true,
"",
"!(_target getVariable ['cpc_flaged', false])",
3,
false,
"",
""
];
Normalement le 2ème bloc, on devrait être bon sur les explications.
Sur le premier, j’utilise simplement la fonction, en m’inscrivant à l’évènement FlagAnimationDone… qu’on aurait du mal à inventer.
Je sors cet évènement directement de la documentation du animateFlag.
Et on ne vient interagir que si notre animation à est 0, soit que le drapeau est en bas (comme le montrait nos tests).
Dans ce cas, on lance la suite, comme on le faisait sur la version saccadée, soit on change le drapeau, et on le remonte.
J’ai seulement ajouté une attente de 1 seconde, en bas du poteau, pour ralentir et simuler légèrement le changement de drapeau.
Conclusion
Avec ça vous avez une version tout à fait fonctionnelle qui respecte le cahier des charges. Marchant en solo comme en serveur dédié, avec une animation fluide et votre drapeau personnalisé !
Je n’ai pas poussé plus que nécessaire chaque étape, mais j’espère que vous avez une vision d’ensemble de ce qui est utilisable, et vers quel élément chercher.
Allez plus loin – Machine à états
Pourquoi faire simple quand on peut faire compliqué ?
La raison est simple, j’ai besoin de complexifier un peu pour des cas comme celui de forcer à le joueur à rester à côté, sinon on stoppe l’animation (ou s’il meurt :))
Mais je ne m’attarderais pas dessus, je vous mets cette version qui fait EXACTEMENT comme la version précédente
[this, "FlagAnimationDone", {
params ["_flag", "_anim"];
switch (_flag getVariable ['cpc_flag_state', 0]) do {
case 1: {
_flag spawn {
_this setVariable ['cpc_flag_state', 2, true];
_this setFlagTexture "images\cpc.jpg";
sleep 1;
[_this, 1, false] call BIS_fnc_animateFlag;
};
};
case 2: {
_flag setVariable ['cpc_flag_state', 3, true];
};
default {};
};
}] call BIS_fnc_addScriptedEventHandler;
this addAction
[
"Hisser le drapeau",
{
params ["_target", "_caller", "_actionId", "_arguments"];
_target setVariable ['cpc_flag_state', 1, true];
[_target, 0, false] call BIS_fnc_animateFlag;
},
[],
1.5,
true,
true,
"",
"(_target getVariable ['cpc_flag_state', 0]) == 0",
3,
false,
"",
""
];