概要
前回レーザーガンっぽい武器を作成しました
今回はレーザ発射時に音声を再生するようにしてみたいと思います
環境
- macOS 15.2
- Java 21.0.5
- forrge MDK 1.20.6-50.1.32
- minecraft 1.20.6
サウンドファイルの配置
1秒程度の ogg 形式のファイルを準備しましょう
レーザっぽい音のフリー素材を見つけてくる感じで OK です
sounds.json の作成
設置した音声リソースを利用する設定を記載します
{
"laser_fire": {
"sounds": [
{
"name": "examplemod:laser_fire",
"volume": 1.0,
"pitch": 1.0
}
]
}
}
SoundEvent の登録
Mod 自体に追加した音を登録する必要があります
アイテム登録などと同じような感じで登録します
- vim src/main/java/com/example/examplemod/ExampleMod.java
主要部分は以下です
import net.minecraft.sounds.SoundEvent;
import net.minecraft.resources.ResourceLocation;
public static final DeferredRegister<SoundEvent> SOUND_EVENTS =
DeferredRegister.create(ForgeRegistries.SOUND_EVENTS, "examplemod");
public static final RegistryObject<SoundEvent> LASER_FIRE_SOUND =
SOUND_EVENTS.register(
"laser_fire",
() ->
SoundEvent.createVariableRangeEvent(
new ResourceLocation("examplemod", "laser_fire")));
public ExampleMod() {
SOUND_EVENTS.register(modEventBus);
}
Mod メイン全体は以下のようにしています
今回と関係ない部分も多いです
package com.example.examplemod;
import com.mojang.logging.LogUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.MapColor;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import org.slf4j.Logger;
@Mod(ExampleMod.MODID)
public class ExampleMod {
public static final String MODID = "examplemod";
private static final Logger LOGGER = LogUtils.getLogger();
public static final DeferredRegister<Block> BLOCKS =
DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);
public static final DeferredRegister<Item> ITEMS =
DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TABS =
DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID);
public static final DeferredRegister<SoundEvent> SOUND_EVENTS =
DeferredRegister.create(ForgeRegistries.SOUND_EVENTS, "examplemod");
public static final RegistryObject<Block> EXAMPLE_BLOCK =
BLOCKS.register(
"example_block",
() -> new Block(BlockBehaviour.Properties.of().mapColor(MapColor.STONE)));
public static final RegistryObject<Block> CUSTOM_BLOCK =
BLOCKS.register("custom_block", CustomBlock::new);
public static final RegistryObject<Item> EXAMPLE_BLOCK_ITEM =
ITEMS.register(
"example_block",
() -> new BlockItem(EXAMPLE_BLOCK.get(), new Item.Properties()));
public static final RegistryObject<Item> LASER_GUN_ITEM =
ITEMS.register("laser_gun", () -> new LaserGunItem(new Item.Properties().stacksTo(1)));
public static final RegistryObject<Item> CUSTOM_BLOCK_ITEM =
ITEMS.register(
"custom_block", () -> new BlockItem(CUSTOM_BLOCK.get(), new Item.Properties()));
public static final RegistryObject<Item> EXAMPLE_ITEM =
ITEMS.register(
"example_item",
() ->
new Item(
new Item.Properties()
.food(
new FoodProperties.Builder()
.alwaysEdible()
.nutrition(1)
.saturationModifier(2f)
.build())));
public static final RegistryObject<SoundEvent> LASER_FIRE_SOUND =
SOUND_EVENTS.register(
"laser_fire",
() ->
SoundEvent.createVariableRangeEvent(
new ResourceLocation("examplemod", "laser_fire")));
public static final RegistryObject<CreativeModeTab> EXAMPLE_TAB =
CREATIVE_MODE_TABS.register(
"example_tab",
() ->
CreativeModeTab.builder()
.withTabsBefore(CreativeModeTabs.COMBAT)
.icon(() -> EXAMPLE_ITEM.get().getDefaultInstance())
.displayItems(
(parameters, output) -> {
output.accept(
EXAMPLE_ITEM
.get());
})
.build());
public ExampleMod() {
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
modEventBus.addListener(this::commonSetup);
BLOCKS.register(modEventBus);
ITEMS.register(modEventBus);
CREATIVE_MODE_TABS.register(modEventBus);
SOUND_EVENTS.register(modEventBus);
MinecraftForge.EVENT_BUS.register(this);
modEventBus.addListener(this::addCreative);
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, Config.SPEC);
}
private void commonSetup(final FMLCommonSetupEvent event) {
LOGGER.info("HELLO FROM COMMON SETUP");
if (Config.logDirtBlock)
LOGGER.info("DIRT BLOCK >> {}", ForgeRegistries.BLOCKS.getKey(Blocks.DIRT));
LOGGER.info(Config.magicNumberIntroduction + Config.magicNumber);
Config.items.forEach((item) -> LOGGER.info("ITEM >> {}", item.toString()));
}
private void addCreative(BuildCreativeModeTabContentsEvent event) {
if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) event.accept(EXAMPLE_BLOCK_ITEM);
if (event.getTabKey() == CreativeModeTabs.COMBAT) event.accept(LASER_GUN_ITEM);
if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) event.accept(CUSTOM_BLOCK_ITEM);
}
@SubscribeEvent
public void onServerStarting(ServerStartingEvent event) {
LOGGER.info("HELLO from server starting");
}
@Mod.EventBusSubscriber(
modid = MODID,
bus = Mod.EventBusSubscriber.Bus.MOD,
value = Dist.CLIENT)
public static class ClientModEvents {
@SubscribeEvent
public static void onClientSetup(FMLClientSetupEvent event) {
LOGGER.info("HELLO FROM CLIENT SETUP");
LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName());
}
}
@Mod.EventBusSubscriber(modid = MODID)
public class BlockEventHandler {
@SubscribeEvent
public static void onBlockBreak(BlockEvent.BreakEvent event) {
Block brokenBlock = event.getState().getBlock();
if (event.getPlayer().isCreative() && brokenBlock == EXAMPLE_BLOCK.get()) {
LOGGER.info("クリエイティブモード中に{}のブロックは削除されません", MODID);
event.setCanceled(true);
}
}
}
@Mod.EventBusSubscriber(modid = MODID, bus = Mod.EventBusSubscriber.Bus.FORGE)
public class BlockPlacementHandler {
@SubscribeEvent
public static void onBlockPlace(BlockEvent.EntityPlaceEvent event) {
Level world = event.getEntity().level();
if (world.isClientSide()) {
return;
}
BlockState placedBlockState = event.getPlacedBlock();
BlockPos placedPos = event.getPos();
if (!(placedBlockState.getBlock() == EXAMPLE_BLOCK.get())) {
return;
}
for (Direction direction : Direction.values()) {
BlockPos neighborPos = placedPos.relative(direction);
BlockState neighborState = world.getBlockState(neighborPos);
if (neighborState.getBlock() == EXAMPLE_BLOCK.get()) {
LOGGER.info("MODブロック同士が連結されました、破壊します");
destroyBlock(world, placedPos);
destroyBlock(world, neighborPos);
return;
}
}
}
private static void destroyBlock(Level world, BlockPos pos) {
BlockState blockState = world.getBlockState(pos);
if (!blockState.isAir()) {
world.destroyBlock(pos, true);
}
}
}
}
右クリックレーザ発射時に音を再生するコードを追加
前回作成した LaserGunItem クラスに音を再生するコードを追加します
Mod メイン側に追加したサウンドイベントから音源を取得し playSound で再生します
音源の再生だけなのでクライアント側の処理かと思いきやサウンドイベント自体が Mod 側のリソースなのでサーバ側で音声を再生するようです
- vim src/main/java/com/example/examplemod/LaserGunItem.java
主要部分は fireLaser メソッドの以下部分です
if (world instanceof ServerLevel serverLevel) {
serverLevel.playSound(
null,
player.blockPosition(),
ExampleMod.LASER_FIRE_SOUND.get(),
SoundSource.PLAYERS,
1.0f,
1.0f
);
LaserGunItem.java 全体のコードは以下の通りです
package com.example.examplemod;
import java.util.List;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.ProjectileUtil;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public class LaserGunItem extends Item {
private float amountOfdamage = 100.0F;
public LaserGunItem(Properties properties) {
super(properties);
}
@Override
public InteractionResultHolder<ItemStack> use(
Level world, Player player, InteractionHand hand) {
if (world.isClientSide()) {
spawnLaserBeam(player);
} else {
fireLaser(world, player);
}
return InteractionResultHolder.sidedSuccess(
player.getItemInHand(hand), world.isClientSide());
}
@Override
public void appendHoverText(
ItemStack stack,
Item.TooltipContext context,
List<Component> tooltip,
TooltipFlag flag) {
super.appendHoverText(stack, context, tooltip, flag);
tooltip.add(
Component.translatable("item.examplemod.laser_gun.damage", amountOfdamage)
.withStyle(ChatFormatting.GREEN));
tooltip.add(
Component.translatable("item.examplemod.laser_gun.description")
.withStyle(ChatFormatting.GRAY));
}
@Override
public boolean onEntityItemUpdate(ItemStack stack, ItemEntity entity) {
return super.onEntityItemUpdate(stack, entity);
}
private void spawnLaserBeam(Player player) {
player.level()
.addParticle(
ParticleTypes.END_ROD,
player.getX(),
player.getEyeY(),
player.getZ(),
player.getLookAngle().x,
player.getLookAngle().y,
player.getLookAngle().z);
}
private void fireLaser(Level world, Player player) {
if (world instanceof ServerLevel serverLevel) {
serverLevel.playSound(
null,
player.blockPosition(),
ExampleMod.LASER_FIRE_SOUND.get(),
SoundSource.PLAYERS,
1.0f,
1.0f
);
}
Vec3 start = player.getEyePosition(1.0F);
Vec3 direction = player.getLookAngle();
double range = 50.0D;
Vec3 end = start.add(direction.scale(range));
HitResult blockHit =
world.clip(
new ClipContext(
start,
end,
ClipContext.Block.OUTLINE,
ClipContext.Fluid.NONE,
player));
EntityHitResult entityHit =
ProjectileUtil.getEntityHitResult(
world,
player,
start,
end,
player.getBoundingBox().expandTowards(direction.scale(range)).inflate(1.0D),
entity -> entity instanceof LivingEntity && entity != player);
if (entityHit != null) {
System.out.println("Hit entity!");
Entity hitEntity = entityHit.getEntity();
if (hitEntity instanceof LivingEntity livingEntity) {
livingEntity.hurt(
livingEntity.damageSources().playerAttack(player), amountOfdamage);
}
} else if (blockHit != null && blockHit.getType() == HitResult.Type.BLOCK) {
System.out.println("Hit block at: " + ((BlockHitResult) blockHit).getBlockPos());
BlockHitResult blockResult = (BlockHitResult) blockHit;
BlockPos hitPos = blockResult.getBlockPos();
world.setBlockAndUpdate(hitPos, Blocks.FIRE.defaultBlockState());
} else {
System.out.println("Missed!");
}
}
}
動作確認
レーザ照射時に追加したサウンドが鳴ることを確認しましょう
最後に
forge Mod で追加したアイテムを使用したした際に効果音を再生する方法を紹介しました
音声形式は ogg ファイルのみサポートしているようなので注意しましょう
参考サイト