Hey tout le monde !
Aujourd'hui, je vais vous montrer une façon de faire un timer avancé, possédant quelques propriétés qui peuvent s'avérer être intéressante !
Mais avant de commencer, faisons-nous déjà notre petit cahier des charges :
Notre timer doit pouvoir posséder deux modes : Incrémentation (compteur) et décrémentation (décompte)
Nous devons pouvoir définir les bornes de notre timer, donc un maximum, et un minimum
Une fonction de boucle sera aussi disponible, elle permettra de relancer le timer automatiquement depuis le début lorsqu'il a atteint sa valeur extremum
Le timer devra être propre au joueur, cela permettra ainsi d'en lancer plusieurs en même temps vu que le stockage se fera sur le joueur en question
Une fonction reset permettra de reset le timer d'un joueur en entier, même en plein milieu du circuit, sans pour autant le faire buguer
Il y aura possibilité de savoir quand est-ce que le timer c'est fini à partir d'un tag
Une fonction de conversion en format minute:seconde devra être disponible
Et le dernier critère, le tout devra fonctionner sans command block (affichage exclut vu qu'il ne fait pas partie du timer)
Comme vous vous en douté, vu que nous avons décider de passer outre les command blocks, le timer se fera essentiellement avec une function.
Commençons par le début ! Il nous faut déjà un moyen d'incrémenter toute les secondes notre score. Pas question de passer par un compteur en tick qui incrémentera un score de 1 à chaque fois qu'il est à 20 comme on le faisait auparavant, c'est sale et il y a surtout moyen de faire beaucoup mieux grâce à la 1.12. Pour cela, nous allons donc utiliser les Advancements !
I/ Actualisation toute les secondes
Pour ceux qui n'ont pas suivi, les advancements sont les successeurs des achievements avec pour principale différence le fait que l'on puisse les créer nous même en JSON. Un advancement (que j'appellerais progrès dans la suite du tutoriel) va requérir ce que l'on appelle un "trigger" (déclencheur en français) qui va être l’événement qui déclenchera notre suite de commande. Or, l'un des événements disponible se nomme "minecraft:location", se déclenchant en fonction des coordonnées du joueur. L'avantage de ce dernier, c'est que l'on n'est pas dans l'obligation d'y entrer des coordonnées de détections (donc que le succès se réalise sans condition) et que l'actualisation de ce dernier se fait toute les secondes, exactement ce que l'on veut !
Alors, allons donc créer notre succès.
Pour cela, rendez-vous dans le dossier de votre map. Vous y trouverez un sous-dossier nommé "data" contenant lui-même un dossier "avancements" que vous allez ouvrir.
Ici, il va falloir créer un dossier dans lequel nous placerons notre succès. Pour ma part, je l'ai nommer "guniverslib" (vu que j'ai avant tout développer ce petit système pour la bibliothèque de ma team), mais vous pouvez lui donner le nom que vous souhaitez. Ce dossier sera ce que l'on appelle le dossier racine. Évitez juste les majuscules et les caractères qui ne sont pas alpha-numérique (donc non a-z, 0-9), cela peut causer quelques bugs.
Une fois dans votre nouveau dossier, il va falloir créer un fichier texte ayant pour extension .json, encore une fois, vous pouvez lui donner le nom que vous souhaitez. Moi, ce sera "timer.json".
Nous allons d'ailleurs tout de suite créer notre function de façon à avoir déjà tout nos outils de travail. Retournez donc dans le dossier "data" puis, cette fois-ci, entrez dans le dossier "functions". Encore une fois, je vous laisse créer un nouveau dossier contenant un fichier en .mcfunction.
Je vous laisse ouvrir les deux fichiers créés avec, je vous le conseil, notepad++.
Bon, le but n'étant pas de faire un tutoriel sur les succès ou la syntaxe JSON, je vous donne donc le code comme cela, tout en vous expliquant tout de même les lignes importantes.
Code (cpp):
{
"criteria" : {
"timer" : {
"trigger" : "minecraft:location"
}
} ,
"rewards" : {
"function" : "Gunivers-Lib:Progress/Timer"
}
}
Bon alors, le "timer", ici, est le nom du déclencheur, vous pouvez y mettre ce que vous voulez, mais pour rester explicite, je le nomme "timer".
Ensuite, avec la clé "trigger", l'on précise le type de déclencheur, donc ici, ce sera celui dont je vous ai parlé tout à l'heure "minecraft:location".
Ce qui suit "rewards" ("récompense" en français) est ce qui se déclenchera quand le progrès sera valider. Ici, c'est donc le déclenchement d'une "function" pour récompense. Pour la valeur, correspondant ici à "Gunivers-Lib:Progress/Timer", vous devez mettre le chemin d'accès à votre fonction sous le format "racine:dossier/fonction"
Par exemple, si ma fonction "a" se trouve dans le dossier "test/mesfonctions" alors je mettrai "test:mesfunctions/a". Si ma fonction se trouve dans la racine, donc dans "test" alors je mettrai "test:a".
Je vous laisse donc mettre votre chemin d'accès !
Voilà, maintenant, on a fini avec notre progrès, vous pouvez donc le sauvegarder et le fermer.
Nous allons passer maintenant au cœur de notre timer, et pour cela, nous allons commencer par faire le compteur !
II/ Compteur
Sur votre map, créez un objectif du nom de timer. C'est dedans que l'on fera notre compteur. Comme nous allons directement intégrer les bornes, faites aussi deux objectifs du nom de "minTimer" qui contiendra la borne minimum et "maxTimer" contenant la borne maximum.
Allez maintenant dans votre fonction.
Avant toutes choses, je vous recommande d'utiliser une coloration syntaxique afin de mieux vous y retrouver dans votre fichier. J'ai fais celui-ci qui pour notepad++, vous n'y êtes pas obligé, mais c'est vivement conseillé, surtout quand vos fichiers prennent de l’ampleur ! N'oubliez pas non plus que tout le contenu de notre fonction s'exécutera toute les secondes !
Faisons un récapitulatif du cahier des charges pour cette partie du système :
Nous avons deux bornes que l'on peut nommer n et m.
Le score du joueur s'incrémente toutes les secondes, partant donc de n et s’arrêtant une fois arriver à m.
Comme nous ne voulons pas que le score s'incrémente en permanence, nous allons faire en sorte que le système s'active sur tout joueur ayant le tag "startIncrement".
La première chose à faire, c'est donc de mettre le score du joueur ayant le tag à sa valeur minimum enregistré dans le scoreboard minTimer, pour cela, un scoreboard operation fera l'affaire :
Code (cpp):
scoreboard players operation @s[ tag= startIncrement] timer = @s[ tag= startIncrement] minTimer
Mais là, nous somme confronté à 2 problèmes :
Le premier est que si le joueur n'a pas de score dans minTimer, cette étape ne va pas fonctionner et cela risque de faire buguer la suite. Pour cela, nous allons faire une ligne au-dessus donnant aux joueurs une valeur par défaut de 0. Pour cela, on ajoute 0 au score du joueur :
Code (cpp):
scoreboard players add @s minTimer 0
Ainsi, il aura systématiquement un score valant 0 si ce dernier n'est pas définis.
Le second problème est que la ligne notre scoreboard operation s'exécutera toute les secondes. Or, cela nous empêche de le modifier et donc de faire notre timer ! Ce que l'on veut, c'est qu'il s'exécute une fois et pas plus !
Donc ce que nous allons faire, c'est lui donner un autre tag qui s'occupera de faire fonctionner tout le reste du système, et lui retirer le tag startIncrement de façon à ce que notre commande ne fonctionne plus à partir de la deuxième boucle vu que le joueur ne possédera plus le tag.
On lui donne le nouveau tag :
Code (cpp):
scoreboard players tag @s[ tag= startIncrement] add increment
Puis l'on supprime l'ancien :
Code (cpp):
scoreboard players tag @s[ tag= startIncrement] remove startIncrement
Voilà, notre "initialisation" est faites ! Maintenant, il faut incrémenter le score. Pour cela, on ajoute tout simplement 1 au score du joueur :
Code (cpp):
scoreboard players add @s[ tag= increment] timer 1
Voilà, notre score s'incrémente bien. Mais il faut aussi qu'il s'arrête une fois que la valeur défini dans "maxTimer" est atteint ! Pour cela, il faudrait que l'on détecte si le score du joueur dans "timer" soit égale à son score dans "maxTimer". Or dans Minecraft, il n'y a pas de moyen directe de faire cela, on va donc devoir tester si la différence entre les deux scores est de 0. Pour faire nos opérations, nous allons passer par un quatrième objectif que je vous invite à créer qui nous servira de scoreboard temporaire, c'est dedans que nous allons faire nos opérations sans avoir à affecter nos autres scores.
Donc, déjà, enregistrons la valeur de maxTimer dans notre objectif cache :
Code (cpp):
scoreboard players operation @s[ tag= increment] Temp = @s[ tag= increment] maxTimer
Ensuite, faisons la soustraction :
Code (cpp):
scoreboard players operation @s[ tag= increment] Temp - = @s[ tag= increment] timer
Ainsi, si la valeur de timer est égale à celle de Temp (donc de maxTimer), alors le résultat de la différence sera 0, si elle est plus grande, la différence sera négative.
Nous allons donc nous servir de ça pour couper notre système en donnant le tag "finish" qui permettra d'annoncer que le timer est fini, puis en retirant le tag "increment" afin de stopper l'incrémentation :
Code (cpp):
scoreboard players tag @s[ tag= ! loop,score_Temp= 0 ] add finish { Tags: [ "increment" ] }
scoreboard players tag @s[ tag= finish] remove increment { Tags: [ "increment" ] }
Comme vous pouvez le voir, on détecte à parti de l'argument "score_Temp" si le score est inférieur ou égale à 0 pour les raisons énoncées plus haut. J'ai rajouté comme argument "tag=!loop", car ici l'on gère le cas où l'on souhaite que le timer ne se fasse qu'une fois, le tag loop nous permettra de dire que l'on veut qu'il se réalise en boucle. Comme l'on ne peut mettre qu'une seule fois "tag" en argument et que j'ai besoin de tester l'absence d'un et le présence de l'autre, alors je met mon le tag dont je souhaite connaitre sa présence en NBT de cette façon là :
Code (cpp):
{ Tags: [ "increment" ] }
Voilà ! Notre incrémentation est fini ! Mais maintenant, on veut aussi ajouter une fonctionnalité qui dira au système de repartir à la valeur minium puis d'incrémenter de nouveau dès que le timer arrive à la valeur maximum. Faire une boucle permanente en bref !
Pour cela, l'on va tout simplement, au lieu de donner le tag "finish", donner au joueur le tag "startIncrement" qui relancera tout le système. Sans oublier bien sûr de lui supprimer le tag "increment" :
Code (cpp):
scoreboard players tag @s[ tag= loop,score_Temp= 0 ] add startIncrement { Tags: [ "increment" ] }
scoreboard players tag @s[ tag= loop,score_Temp= 0 ] remove increment
Maintenant, le tout est fini !
Voici à quoi ressemble nomalement votre incrémentation :
Je vous conseil vivement d'annoter votre fonction à l'aide de # afin de savoir à quoi sert telle partie.
Maintenant, passons à la décrémentation !
III/ Décrémentation
Bon, je ne vais pas trop m'attarder ici car c'est exactement la même chose qu'au dessus.
Voici le résultat :
Les seuls choses qui changent sont de prime abord, les noms des tags.
Ensuite, l'on part non pas de minTimer mais de maxTimer à la ligne 32,
le remove à la place du add à la ligne 34,
et le "score_Temp_min" au lieu du "score_Temp" pour argument, du fait que je soustrait la valeur la plus petite par la plus grande. Donc lorsque le score timer atteindra ou dépassera la plus petite (donc que le score soit plus petit que minTimer), la différence sera donc positive ou nul.
Voilà !
Maintenant, l'on va passer à notre partie qui convertira notre score timer en un format m:s.
IV/ Formatage en m:s
Pour vous expliquer un peu le principe, si l'on souhaite afficher le timer en actionbar il est plus propre d'avoir à la place de "65" (secondes) "1:5" qui est donc le nombre de minutes:le nombre de secondes.
Pour cela, nous allons utiliser un signe opératoire assez peu utilisé, le modulo.
Si vous ne savez pas ce que cela est, il s'agit du reste de la division euclidienne (division entière) :
Ici, le résultat de 42 modulo 4 (que l'on écrit 42 % 4) est 2.
On peut donc dire que 4 (diviseur) * 10 (résultat) + 2 (reste) = 42 (dividende).
Vous ne voyez pas à quoi ça va servir ?
Et bah faisons 75 % 60. Le résultat sera 15, soit le nombre de secondes après 60 (une minute) !
Voici donc notre façon de récupérer nos secondes, mais qu'en est-il pour les minutes ?
Bah, il suffit simplement de récupérer non pas le reste mais le résultat de notre division entière correspondant au nombre de minutes.
Ainsi, si l'on a un nombre valant 432, nous obtiendrons grâce à la division euclidienne un résultat de 7 (donc nos minutes) et un reste de 12 (nos secondes).
Si l'on vérifie, 7*60+12 est bien égale à 432 !
C'est bien beau tout ça, mais comment obtient-on le résultat de notre division euclidienne ?
Bah c'est simple, dans minecraft, les scoreboards ne stock que des nombres entiers, donc le résultat de la division /= sera le résultat de notre division euclidienne !
Voici donc ce que cela donne dans Minecraft :
Code (cpp):
scoreboard players operation @s[ tag= displayTimer] minuteTimer = @s[ tag= displayTimer] timer
scoreboard players operation @s[ tag= displayTimer] minuteTimer / = 60 Constant
scoreboard players operation @s[ tag= displayTimer] secondTimer = @s[ tag= displayTimer] timer
scoreboard players operation @s[ tag= displayTimer] secondTimer % = 60 Constant
On va donc faire en sorte que nos calculs ne se fassent que si le joueur possède le tag displayTimer afin de n'activer cette partie que si besoin. Nous stockerons nos minutes dans un scoreboard "minuteTimer" et les secondes dans "secondTimer". Un dernier scoreboard est donc créés, nommé "Constant", dans lequel l'on va mettre un faux-joueur du nom de 60 contenant la valeur 60 nous permettant ainsi de faire notre modulo 60.
Je créés ce faux-joueur tout en haut de ma fonction grâce à cette commande :
Code (cpp):
scoreboard players set 60 Constant 60
Bon, ça fini, nous allons pouvoir finir notre fonction par notre système de reset !
V/ Le reset
Alors, là, rien de bien compliquer, lorsque le joueur a le tag "reset", on lui supprime tout ces tags (sauf loop et displayTimer) puis on lui met son score timer à 0 avant de supprimer le tag "reset" :
Code (cpp):
scoreboard players set @s[ tag= reset] timer 0
scoreboard players tag @s[ tag= reset] remove finish
scoreboard players tag @s[ tag= reset] remove increment
scoreboard players tag @s[ tag= reset] remove decrement
scoreboard players tag @s[ tag= reset] remove startIncrement
scoreboard players tag @s[ tag= reset] remove startDecrement
scoreboard players tag @s[ tag= reset] remove reset
Il ne nous reste plus qu'à mettre une ligne, permettant de refaire fonctionner notre progrès (car une fois déclencher, ce dernier ce verrouille, il faut donc le réactiver) :
Code (cpp):
advancement revoke @s only guniverslib: timer
Je vous laisse remplacer "guniverslib:timer" par votre chemin d'accès à votre progrès
Voici donc le résultat final de votre fonction :
Maintenant, plus qu'à tester le résultat !
Vous pouvez tester le displayTimer grâce à cette commande :
Code (cpp):
/ execute @a ~ ~ ~ title @s actionbar [ "" ,{ "text" : "Timer: " ,"color" : "dark_green" } ,{ "score" : { "name" : "@s" ,"objective" : "minuteTimer" } ,"color" : "red" } ,{ "text" : ":" ,"color" : "gold" } ,{ "score" : { "name" : "@s" ,"objective" : "secondTimer" } ,"color" : "red" } ]
Pour ne pas faire beaucoup plus de texte, et pour un peu de détente, je vous montre ce que cela donne en vidéo :
Merci d'avoir lu ce tutoriel et j'espère que cela vous sera utile !