概要
- マイクラ×DTMの夢の可能性
- クリスマスソングを作成してみる
- 基本的に25音に制限されているので表現できる曲は限られてくると感じる
コード
MusicBoxBlockの作成
MusicBoxBlock.java
package com.example.examplemod.music_box;
import com.example.examplemod.ExampleMod;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.phys.BlockHitResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class MusicBoxBlock extends BaseEntityBlock implements EntityBlock {
public MusicBoxBlock(){
super(BlockBehaviour.Properties.of(Material.STONE));
}
@Override
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHit) {
if(!pLevel.isClientSide){
BlockEntity blockEntity=pLevel.getBlockEntity(pPos);
if(blockEntity instanceof MusicBoxBlockEntity musicBoxBlockEntity){
musicBoxBlockEntity.setPlaying(!musicBoxBlockEntity.getPlaying());
if(musicBoxBlockEntity.getPlaying())
{
pPlayer.displayClientMessage(new TextComponent("再生します"),true);
}
else
{
pPlayer.displayClientMessage(new TextComponent("停止します"),true);
}
//変更をクライアントに同期
musicBoxBlockEntity.setChanged();
pLevel.sendBlockUpdated(pPos,pState,pState,3);
}
}
return InteractionResult.SUCCESS;
}
@Override
public @NotNull RenderShape getRenderShape(@NotNull BlockState pState) {
return RenderShape.MODEL;
}
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
return new MusicBoxBlockEntity(pPos,pState);
}
@Nullable
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level pLevel, BlockState pState, BlockEntityType<T> pBlockEntityType) {
return createTickerHelper(pBlockEntityType, ExampleMod.MUSIC_BOX_BLOCK_ENTITY,MusicBoxBlockEntity::tick);
}
}
MusicBoxBlockEntityの作成
MusicBoxBlockEntity.java
package com.example.examplemod.music_box;
import com.example.examplemod.ExampleMod;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.NotNull;
public class MusicBoxBlockEntity extends BlockEntity {
private int songId;
private boolean isPlaying=false;
private int songProgressTick=0;
public MusicBoxBlockEntity(BlockPos pPos, BlockState pState){
super(ExampleMod.MUSIC_BOX_BLOCK_ENTITY,pPos,pState);
}
@Override
protected void saveAdditional(@NotNull CompoundTag pTag) {
super.saveAdditional(pTag);
pTag.putInt("SongId",this.songId);
pTag.putInt("SongProgress",this.songProgressTick);
pTag.putBoolean("IsPlaying",this.isPlaying);
}
@Override
public void load(@NotNull CompoundTag pTag) {
super.load(pTag);
this.songId=pTag.getInt("SongId");
this.songProgressTick=pTag.getInt("SongProgress");
this.isPlaying=pTag.getBoolean("IsPlaying");
}
@Override
public @NotNull CompoundTag getUpdateTag() {
CompoundTag tag=new CompoundTag();
saveAdditional(tag);//現在のデータをNBTに保存
return tag;
}
@Override
public void handleUpdateTag(CompoundTag tag) {
load(tag);//受け取ったNBTでデータを読み込み
}
public static void tick(Level level, BlockPos pos, BlockState state, MusicBoxBlockEntity blockEntity) {
if (!blockEntity.isPlaying || level.isClientSide) {
return;
}
SongData currentSong=MusicLibrary.SONGS.get(blockEntity.songId);
if(currentSong==null){
blockEntity.isPlaying=false;
return;
}
for(NoteData note: currentSong.notes){
if(note.startTick==blockEntity.songProgressTick){
float pitch=(float) Math.pow(2.0D, (double) (note.pitch-12)/12.0D);
level.playSound(null,pos,SoundEvents.NOTE_BLOCK_HARP,SoundSource.RECORDS,1.0F,pitch);
if(level instanceof ServerLevel serverLevel){
serverLevel.sendParticles(ParticleTypes.NOTE,
pos.getX()+0.5D,
pos.getY()+1.2D,
pos.getZ()+0.5D,
1,
(double) note.pitch/24.0D,
0.0D,
0.0D,
1.0D
);
}
}
}
blockEntity.songProgressTick++;
}
public int getSongId(){
return this.songId;
}
public boolean getPlaying(){
return this.isPlaying;
}
public void setSongId(int id){
this.songId=id;
}
public void setPlaying(boolean is){
this.isPlaying=is;
if(this.isPlaying){
this.songProgressTick=0;
}
}
}
NoteDataクラスの作成
音階といつ鳴らすかのデータが入ります。
NoteData.java
package com.example.examplemod.music_box;
public class NoteData {
public final int pitch;
public final int startTick;
public NoteData(int pitch, int startTick){
this.pitch=pitch;
this.startTick=startTick;
}
}
SongDataクラスの作成
NoteDataクラスのリストと、全体の再生時間を保持します。
SongData.java
package com.example.examplemod.music_box;
import java.util.List;
public class SongData {
public final List<NoteData> notes;
public final int totalTicks;
public SongData(List<NoteData> notes, int totalTicks){
this.notes=notes;
this.totalTicks=totalTicks;
}
}
MusicLibraryクラスの作成
今後複数曲に対応したいのでIntegerとSongDataをMapで保持するクラスを作成します。
MusicLibrary.java
package com.example.examplemod.music_box;
import java.util.List;
import java.util.Map;
public class MusicLibrary {
//曲IDと楽譜データのマップ
public static final Map<Integer, SongData> SONGS=Map.of(
0, new SongData(
List.of(
new NoteData(13, 0),
new NoteData(18, 10),new NoteData(6,10),
new NoteData(18, 20),
new NoteData(20, 25),
new NoteData(18, 30), new NoteData(6, 30),
new NoteData(17, 35),
new NoteData(15, 40), new NoteData(11, 40),
new NoteData(15, 50),
new NoteData(15, 60), new NoteData(11, 60),
new NoteData(20, 70), new NoteData(8, 70),
new NoteData(20, 80),
new NoteData(22,85),
new NoteData(20,90), new NoteData(13,90),
new NoteData(18,95),
new NoteData(17, 100), new NoteData(13, 100),
new NoteData(13, 110),
new NoteData(13, 120), new NoteData(13, 120),
new NoteData(22, 130), new NoteData(10, 130),
new NoteData(22, 140),
new NoteData(23, 145),
new NoteData(22,150), new NoteData(10,150),
new NoteData(20,155),
new NoteData(18, 160), new NoteData(15,160),
new NoteData(15, 170),
new NoteData(13, 180), new NoteData(15,180),
new NoteData(13,185),
new NoteData(15,190), new NoteData(11,190),
new NoteData(20,200),
new NoteData(17,210), new NoteData(13,210),
new NoteData(18,220), new NoteData(6,220),
new NoteData(13,240), new NoteData(6,240),
new NoteData(18, 250), new NoteData(6,250),
new NoteData(18,260),
new NoteData(18, 270), new NoteData(18, 270),
new NoteData(17, 280), new NoteData(13,280),
new NoteData(17, 300), new NoteData(13, 300),
new NoteData(18, 310), new NoteData(11,310),
new NoteData(17,320),
new NoteData(15,330), new NoteData(11,330),
new NoteData(13,340), new NoteData(13,340),
new NoteData(10,360), new NoteData(13,360),
new NoteData(11,370), new NoteData(11,370),
new NoteData(10,380),
new NoteData(8,390), new NoteData(11,390),
new NoteData(13,400), new NoteData(10,400),
new NoteData(13,410),
new NoteData(13,420), new NoteData(10,420),
new NoteData(13,425),
new NoteData(15,430), new NoteData(11,430),
new NoteData(20, 440), new NoteData(13, 440),
new NoteData(17,450), new NoteData(8,450),
new NoteData(18,460), new NoteData(6,460)
),
480 // この曲の総Tick数 (16小節 * 30tick/小節)
),
1, new SongData(
List.of(
new NoteData(13,0),
new NoteData(13,10),
new NoteData(15,20),
new NoteData(15,30)
),
40
)
);
}