Appearance
L'utilisation d'un item sur un bloc
Bienvenue dans cours de modding de Minecraft avec Forge 1.20.1. Jusqu'à présent, nous avons appris à manipuler les aliments, à créer des armes puissantes, et à gérer la synchronisation entre le client et le serveur lors d'un clic droit dans le vide (méthode use).
Aujourd'hui, nous allons franchir une étape cruciale : interagir directement avec les blocs de notre monde. Qu'il s'agisse de transformer de la terre en un bloc magique, d'allumer un feu personnalisé ou de récolter une ressource spécifique, tout repose sur une seule et unique méthode système : useOn.
1. Comprendre le concept de useOn
Dans Minecraft, lorsque vous faites un clic droit avec un objet à la main, le jeu doit d'abord déterminer si vous visez un élément physique de l'environnement ou si vous cliquez simplement "dans le vide".
La méthode
use(...): Se déclenche de manière globale lorsque le joueur utilise l'objet, principalement sans bloc ciblé (comme manger ou bander un arc).La méthode
useOn(...): Ne s'exécute que si et seulement si le curseur (crosshair) du joueur pointe directement un bloc à portée de main au moment du clic droit. C'est le comportement typique de la houe sur la terre ou du briquet sur l'obsidienne.
Analogie pour débutant : Le Rapport de Police
Imaginez que la méthode useOn soit un officier de police appelé sur une scène d'action. Minecraft ne vous donne pas les variables en vrac. À la place, il vous tend une mallette fermée contenant un rapport complet.
Cette mallette, c'est l'objet UseOnContext (contexte d'utilisation). À l'intérieur, vous y trouverez toutes les preuves : qui a cliqué, sur quel bloc, de quel côté du bloc, et dans quel monde !
2. Décortiquer l'objet UseOnContext
Pour exploiter la méthode useOn, nous devons inspecter les méthodes d'extraction du contexte UseOnContext (souvent abrégé en ctx ou context dans le code).
| Méthode d'extraction | Type de retour | Description / Utilité |
|---|---|---|
context.getLevel() | Level | Récupère le monde actuel. Crucial pour faire notre vérification de sécurité level.isClientSide. |
context.getClickedPos() | BlockPos | Donne les coordonnées exactes (X, Y, Z) du bloc sur lequel le joueur a cliqué. |
context.getPlayer() | Player | Récupère l'entité du joueur qui a effectué l'action (peut être null dans certains cas rares d'automatisation). |
context.getItemInHand() | ItemStack | Représente l'exemplaire précis de l'objet actuellement tenu (utile pour réduire sa durabilité ou consommer l'item). |
context.getClickedFace() | Direction | Indique sur quelle face du bloc le clic a eu lieu (le dessus UP, le dessous DOWN, le Nord, le Sud, etc.). |
3. Structure et Implémentation du Code
Créons un exemple concret : un outil personnalisé appelé BatonAlchimiqueItem. Lorsqu'on fait un clic droit sur un bloc de LOG (Bûche de chêne), il le transforme instantanément en bloc d'OR, joue un son de transformation et applique des dégâts d'usure à notre objet.
java
package com.monmod.items;
import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.*;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
public class BatonAlchimiqueItem extends Item {
public BatonAlchimiqueItem(Properties properties) {
super(properties);
}
@Override
public InteractionResult useOn(UseOnContext context) {
// 1. Extraire les données indispensables de la mallette "context"
Level level = context.getLevel();
BlockPos positionCliquee = context.getClickedPos();
Player joueur = context.getPlayer();
// 2. Déterminer quel type de bloc a été touché
BlockState blocTouche = level.getBlockState(positionCliquee);
// Sécurité : S'assurer qu'un joueur valide a fait l'action
if (joueur == null) {
return InteractionResult.PASS;
}
// 3. Condition : Est-ce du bois de chêne (Bûche)?
if (blocTouche.is(Blocks.OAK_LOG)) {
// 4. Gestion de la barrière asynchrone (Cours 9) : Actions physiques côté Serveur
if (!level.isClientSide) {
// Remplacer le bloc par un bloc d'or
level.setBlockAndUpdate(positionCliquee, Blocks.GOLD_BLOCK.defaultBlockState());
// Jouer un effet sonore à cet endroit précis
level.playSound(null, positionCliquee, SoundEvents.AMETHYST_BLOCK_CHIME,
SoundSource.BLOCKS, 1.0F, 1.0F);
// Appliquer 1 point de dégât de durabilité à l'item tenu
context.getItemInHand().hurtAndBreak(1, joueur, (p) -> p.broadcastBreakEvent(context.getHand()));
}
// On informe Minecraft que l'action a réussi (bloque l'animation par défaut)
return InteractionResult.SUCCESS;
}
// Si on clique sur autre chose qu'une bûche de chêne, on laisse passer sans rien faire
return InteractionResult.PASS;
}
}Le Piège Absolu : Ne pas modifier le monde côté Client ! Si vous oubliez la condition
if (!level.isClientSide)avant d'appeler la méthodelevel.setBlockAndUpdate(...), le bloc d'or apparaîtra visuellement sur l'écran du joueur, mais il sera totalement invisible et inexistant pour le serveur. Dès que le joueur tentera de marcher dessus ou de recharger sa partie, le bloc réapparaîtra magiquement sous forme de bûche de chêne. C'est le fameux bug du "bloc fantôme".
4. Les valeurs de retour de InteractionResult
À la fin de votre méthode useOn, vous devez impérativement renvoyer un état à Minecraft pour lui dire comment s'est déroulée l'interaction:
InteractionResult.SUCCESS: L'action s'est bien déroulée. Le jeu joue l'animation de balancement du bras (swing) et arrête de chercher d'autres interactions.InteractionResult.CONSUME: Semblable àSUCCESS, souvent utilisé côté serveur pour valider la consommation d'une ressource.InteractionResult.PASS: Rien ne s'est passé. Le jeu va continuer à chercher si le bloc lui-même possède une action prioritaire (comme ouvrir un coffre).InteractionResult.FAIL: L'action a explicitement échoué. Annule toute autre tentative d'interaction.
Exercice Pratique Obligatoire
Pour valider la compréhension de ce cours, implémentez l'exercice suivant:
Objectif : Le Bâton de Jardinier Fertile
Créez un item nommé
BatonJardinierItem.Lorsque le joueur fait un clic droit sur un bloc de Terre standard (
Blocks.DIRT):
Le bloc doit se transformer instantanément en Bloc d'Herbe (
Blocks.GRASS_BLOCK).Un effet de particules de type Bone Meal doit apparaître (Astuce : utilisez
level.levelEvent(2005, positionCliquee, 0);).
- Si le joueur fait un clic droit sur la face supérieure (
UP) d'un bloc d'Herbe:
Faites apparaître un bloc de Pissenlit (
Blocks.DANDELION) juste au-dessus du bloc ciblé (utilisez la méthodepositionCliquee.above()).Attention à vérifier que le bloc au-dessus est bien de l'air !