You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Minecraft Fabric 1.21.1模组开发:如何阻止玩家实体播放指定声音(Vibral模组需求)

Minecraft Fabric 1.21.1模组开发:如何阻止玩家实体播放指定声音(Vibral模组需求)

嘿,我看了你这段Vibral模组的Mixin代码,问题主要出在对声音拦截的逻辑太“一刀切”,而且没有精准区分你要拦截的「环境音」和「主动工具音」场景。咱们一步步调整,让逻辑更精准,同时满足你的需求:


一、现有代码的核心问题

  1. PlayerEntityMixin里的playSound注入太粗暴:直接在HEAD就cancel所有声音,会把玩家的所有声音(比如喝药水、穿盔甲的提示音)都拦截,这显然不是你要的效果
  2. 没有区分「穿盔甲」和「持工具」的不同拦截规则:目前只处理了游泳声音,还没覆盖走路、进食、工具使用等核心场景
  3. EntityMixin的代码冗余且不完整:玩家的游泳声音逻辑完全可以在PlayerEntityMixin里处理,没必要在EntityMixin里重复写,而且你贴的EntityMixin代码还截断了

二、修正后的完整代码

1. 优化后的PlayerEntityMixin

这个Mixin负责处理穿全套Vibral盔甲时的环境音拦截,以及持Vibral工具时的主动操作音拦截,逻辑更精准:

package net.zadezapper.vibral.mixin;

import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundEvent;
import net.zadezapper.vibral.item.ModItems;
import net.zadezapper.vibral.sound.ModSoundEvents;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(PlayerEntity.class)
public abstract class PlayerEntityMixin {

    // 直接获取当前玩家实例,无需多余强转
    @Unique
    private final PlayerEntity self = (PlayerEntity) this;

    // ==============================================
    // 场景1:穿全套Vibral盔甲时,拦截环境类声音
    // ==============================================

    // 拦截游泳/浮水声音
    @Inject(at = @At("HEAD"), method = "getSwimSound", cancellable = true)
    public void interceptSwimSound(CallbackInfoReturnable<SoundEvent> cir) {
        if (isWearingFullVibralArmor(self)) {
            cir.setReturnValue(ModSoundEvents.SILENT);
        }
    }

    // 拦截走路/跑步声音
    @Inject(at = @At("HEAD"), method = "getWalkSound", cancellable = true)
    public void interceptWalkSound(CallbackInfoReturnable<SoundEvent> cir) {
        if (isWearingFullVibralArmor(self)) {
            cir.setReturnValue(ModSoundEvents.SILENT);
        }
    }

    // 拦截进食/喝水声音
    @Inject(at = @At("HEAD"), method = "getEatSound", cancellable = true)
    public void interceptEatSound(ItemStack stack, CallbackInfoReturnable<SoundEvent> cir) {
        if (isWearingFullVibralArmor(self)) {
            cir.setReturnValue(ModSoundEvents.SILENT);
        }
    }

    // 拦截望远镜使用声音
    @Inject(at = @At("HEAD"), method = "getSpyglassSound", cancellable = true)
    public void interceptSpyglassSound(CallbackInfoReturnable<SoundEvent> cir) {
        if (isWearingFullVibralArmor(self)) {
            cir.setReturnValue(ModSoundEvents.SILENT);
        }
    }

    // ==============================================
    // 场景2:手持Vibral工具时,拦截主动操作音
    // ==============================================

    // 拦截近战攻击声音
    @Inject(at = @At("HEAD"), method = "getAttackSound", cancellable = true)
    public void interceptAttackSound(CallbackInfoReturnable<SoundEvent> cir) {
        if (isHoldingVibralTool(self)) {
            cir.setReturnValue(ModSoundEvents.SILENT);
        }
    }

    // 拦截挖掘方块的声音
    @Inject(at = @At("HEAD"), method = "getDiggingSound", cancellable = true)
    public void interceptDiggingSound(CallbackInfoReturnable<SoundEvent> cir) {
        if (isHoldingVibralTool(self)) {
            cir.setReturnValue(ModSoundEvents.SILENT);
        }
    }

    // ==============================================
    // 工具方法:判断是否穿全套Vibral盔甲
    // ==============================================
    @Unique
    private boolean isWearingFullVibralArmor(LivingEntity entity) {
        return entity.getEquippedStack(EquipmentSlot.HEAD).isOf(ModItems.VIBRAL_HELMET)
                && entity.getEquippedStack(EquipmentSlot.CHEST).isOf(ModItems.VIBRAL_CHESTPLATE)
                && entity.getEquippedStack(EquipmentSlot.LEGS).isOf(ModItems.VIBRAL_LEGGINGS)
                && entity.getEquippedStack(EquipmentSlot.FEET).isOf(ModItems.VIBRAL_BOOTS);
    }

    // ==============================================
    // 工具方法:判断是否手持Vibral工具
    // ==============================================
    @Unique
    private boolean isHoldingVibralTool(LivingEntity entity) {
        ItemStack mainHand = entity.getEquippedStack(EquipmentSlot.MAINHAND);
        return mainHand.isOf(ModItems.VIBRAL_SWORD)
                || mainHand.isOf(ModItems.VIBRAL_PICKAXE)
                || mainHand.isOf(ModItems.VIBRAL_AXE)
                || mainHand.isOf(ModItems.VIBRAL_SHOVEL)
                || mainHand.isOf(ModItems.VIBRAL_HOE);
    }
}

2. 简化后的EntityMixin(可选)

如果不需要给其他穿盔甲的生物加静音效果,这个Mixin可以直接删掉;如果需要保留,就用下面的简化版:

package net.zadezapper.vibral.mixin;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.sound.SoundEvent;
import net.zadezapper.vibral.item.ModItems;
import net.zadezapper.vibral.sound.ModSoundEvents;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(Entity.class)
public abstract class EntityMixin {

    @Unique
    private final Entity self = (Entity) this;

    // 给穿全套Vibral盔甲的生物拦截游泳声音
    @Inject(at = @At("HEAD"), method = "getSwimSound", cancellable = true)
    public void interceptEntitySwimSound(CallbackInfoReturnable<SoundEvent> cir) {
        if (self instanceof LivingEntity livingEntity) {
            boolean fullArmor = livingEntity.getEquippedStack(EquipmentSlot.HEAD).isOf(ModItems.VIBRAL_HELMET)
                    && livingEntity.getEquippedStack(EquipmentSlot.CHEST).isOf(ModItems.VIBRAL_CHESTPLATE)
                    && livingEntity.getEquippedStack(EquipmentSlot.LEGS).isOf(ModItems.VIBRAL_LEGGINGS)
                    && livingEntity.getEquippedStack(EquipmentSlot.FEET).isOf(ModItems.VIBRAL_BOOTS);
            if (fullArmor) {
                cir.setReturnValue(ModSoundEvents.SILENT);
            }
        }
    }
}

三、关键修改点解释

  1. 放弃一刀切拦截playSound:转而拦截生成特定声音的方法(比如getWalkSoundgetEatSound),这样只会拦截你需要的声音类型,不会影响其他正常游戏声音(比如玩家受伤、打开箱子的声音)
  2. 分场景处理逻辑:明确区分「穿盔甲拦环境音」和「持工具拦操作音」的不同规则,每个规则对应单独的注入方法,逻辑更清晰
  3. 简化实例引用:直接用(PlayerEntity) this获取当前玩家实例,不需要额外定义冗余的entity变量
  4. 抽离工具方法:把盔甲和工具判断抽成独立的工具方法,代码更整洁,后续扩展新的盔甲/工具也更方便

四、额外的补充说明

  1. 关于ModSoundEvents.SILENT的定义:确保你定义的静音事件是一个无音频的空事件,在ModSoundEvents里注册:
public static final SoundEvent SILENT = SoundEvent.of(new Identifier("vibral", "silent"));

然后在assets/vibral/sounds.json里配置(不需要音频文件):

{
  "silent": {
    "subtitle": "sound.vibral.silent",
    "sounds": []
  }
}
  1. 工具使用声音的额外处理:如果有些工具声音是由Item类触发的(比如斧头砍树的特殊声音),可以额外Mixin到Item类的mineBlock方法,判断是否为Vibral工具后拦截声音
  2. Mixin优先级:除非需要和其他模组的Mixin竞争执行顺序,否则不需要设置priority = 4096这种极端值,默认优先级就足够了

这样修改后,你的Vibral模组就能精准拦截指定的声音,同时不影响其他正常的游戏体验啦!

火山引擎 最新活动