概要
コード
BaseEntityBlockを継承し、EntityBlockを実装したクラスを作成
後々BlockEntityで紐付けます。
eatの内部にある、変数iで食べた回数を監視するところは適宜食べさせたい回数を指定します。
package com.example.examplemod.birthdaycake;
import com.example.examplemod.ExampleMod;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.stats.Stats;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.*;
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.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static net.minecraft.world.level.block.CakeBlock.getOutputSignal;
public class BirthdayCake extends BaseEntityBlock implements EntityBlock{
//食べた回数
public static final IntegerProperty BITES = BlockStateProperties.BITES;
//ろうそくに着火されているかいないか
public static final BooleanProperty LIT=BlockStateProperties.LIT;
//食べて行った時の当たり判定(だと思ってる。)ちょっと適当。
protected static final VoxelShape[] SHAPE_BY_BITE = new VoxelShape[]
{
Block.box(1.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D),
Block.box(3.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D),
Block.box(5.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D),
Block.box(7.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D),
Block.box(9.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D),
Block.box(11.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D),
Block.box(13.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D)
};
public static final IntegerProperty CANDLES=IntegerProperty.create("candles",0,2);
public BirthdayCake(){
super(BlockBehaviour.Properties.of(Material.CAKE)
.lightLevel(state->state.getValue(LIT) ? 15 : 0));
//デフォルトの状態の設定
this.registerDefaultState(this.getStateDefinition().any()
.setValue(BITES,0)
.setValue(CANDLES,0)
.setValue(LIT,false));
}
public @NotNull VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) {
return SHAPE_BY_BITE[pState.getValue(BITES)];
}
@Override
public RenderShape getRenderShape(BlockState pState) {
return RenderShape.MODEL;
}
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHit) {
ItemStack itemstack = pPlayer.getItemInHand(pHand);
if(itemstack.getItem() instanceof NumberCandleItem){
int candleCount=pState.getValue(CANDLES);
//BlockEntityを取得
BlockEntity blockEntity=pLevel.getBlockEntity(pPos);
if(blockEntity instanceof BirthdayCakeBlockEntity cakeBlockEntity){
if(candleCount<2){
//1本目(左側)
if(candleCount==0){
cakeBlockEntity.setCandleLeft(itemstack);
pLevel.setBlock(pPos,pState.setValue(CANDLES,1),3);
}
//2本目(右側)
else{
cakeBlockEntity.setCandleRight(itemstack);
pLevel.setBlock(pPos,pState.setValue(CANDLES,2),3);
}
//アイテム消費と音の演出
if(!pPlayer.isCreative()){
itemstack.shrink(1);
}
pLevel.playSound(null,pPos,SoundEvents.CAKE_ADD_CANDLE,SoundSource.BLOCKS,1.0F,1.0F);
return InteractionResult.SUCCESS;
}
}
}
//手に持っているアイテムが火打石と打鉄であった場合
if(itemstack.is(Items.FLINT_AND_STEEL)){
if(!pLevel.isClientSide){
BlockEntity blockEntity=pLevel.getBlockEntity(pPos);
if(blockEntity instanceof BirthdayCakeBlockEntity cakeEntity){
if(!cakeEntity.isLitLeft() || !cakeEntity.isLitRight()){
pLevel.playSound(null, pPos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS,1.0F,1.0F);
cakeEntity.setLit(true,true);
pLevel.setBlock(pPos,pState.setValue(LIT,true),3);
itemstack.hurtAndBreak(1,pPlayer,(player) -> player.broadcastBreakEvent(pHand));
return InteractionResult.SUCCESS;
}
}
}else{
pPlayer.displayClientMessage(new TextComponent("誕生日おめでとう!!"),true);
}
return InteractionResult.PASS;
}
if (pLevel.isClientSide) {
if (eat(pLevel, pPos, pState, pPlayer).consumesAction()) {
return InteractionResult.SUCCESS;
}
if (itemstack.isEmpty()) {
return InteractionResult.CONSUME;
}
}
return eat(pLevel, pPos, pState, pPlayer);
}
@Nullable
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level pLevel, BlockState pState, BlockEntityType<T> pBlockEntityType) {
return createTickerHelper(pBlockEntityType,ExampleMod.BIRTHDAY_CAKE_ENTITY,BirthdayCakeBlockEntity::tick);
}
protected static InteractionResult eat(LevelAccessor pLevel, BlockPos pPos, BlockState pState, Player pPlayer) {
if (!pPlayer.canEat(false)) {
return InteractionResult.PASS;
} else {
pPlayer.awardStat(Stats.EAT_CAKE_SLICE);
pPlayer.getFoodData().eat(2, 0.1F);
int i = pState.getValue(BITES);
pLevel.gameEvent(pPlayer, GameEvent.EAT, pPos);
if (i < 5) {
pLevel.setBlock(pPos, pState.setValue(BITES, Integer.valueOf(i + 1)), 3);
} else {
pLevel.removeBlock(pPos, false);
pLevel.gameEvent(pPlayer, GameEvent.BLOCK_DESTROY, pPos);
}
return InteractionResult.SUCCESS;
}
}
public BlockState updateShape(BlockState pState, Direction pFacing, BlockState pFacingState, LevelAccessor pLevel, BlockPos pCurrentPos, BlockPos pFacingPos) {
return pFacing == Direction.DOWN && !pState.canSurvive(pLevel, pCurrentPos) ? Blocks.AIR.defaultBlockState() : super.updateShape(pState, pFacing, pFacingState, pLevel, pCurrentPos, pFacingPos);
}
public boolean canSurvive(BlockState pState, LevelReader pLevel, BlockPos pPos) {
return pLevel.getBlockState(pPos.below()).getMaterial().isSolid();
}
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> pBuilder) {
pBuilder.add(BITES, CANDLES, LIT);
}
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
return new BirthdayCakeBlockEntity(pPos,pState);
}
}
BlockEntityクラスの作成
ろうそくが立っているかどうか、点火されているかどうか、効果の持続時間などをNBTに保存しています。
また、今回バースデイソングを音符ブロックのノーツで演奏してみるということに挑戦してみました。
音階と音の拍を整数型の配列に格納し、tickメソッドで呼び出しています。
簡単な曲なら弾けそうな感じがします。
参考サイト↓
https://minecraft.fandom.com/ja/wiki/%E9%9F%B3%E7%AC%A6%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF
package com.example.examplemod.birthdaycake;
import com.example.examplemod.ExampleMod;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
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.Nullable;
import java.util.Objects;
public class BirthdayCakeBlockEntity extends BlockEntity {
//左右の蝋燭の情報をItemStackとして保持する
private ItemStack candleLeft=ItemStack.EMPTY;
private ItemStack candleRight=ItemStack.EMPTY;
//左右の着火状況をBool値として保持する
private boolean litLeft=false;
private boolean litRight=false;
private int effectTicks=0;
private static final int EFFECT_DURATION=300;
private int musicIndex=0;
private int nextNoteTick=0;
private static final int[] NOTE_PITCHES = { //音階(ドキュメント参照→https://minecraft.fandom.com/ja/wiki/%E9%9F%B3%E7%AC%A6%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF)
6, 6, 8, 6, 11, 10,
6, 6, 8, 6, 13, 11,
6, 6, 18, 15, 11, 10, 8,
16, 16, 15, 11, 13, 11
};
private static final int[] NOTE_DELAYS = { // 各音符の長さ (tick単位、12で1拍)
12, 6, 12, 12, 12, 24,
12, 6, 12, 12, 12, 24,
12, 6, 12, 12, 12, 12, 24,
12, 6, 12, 12, 12, 24
};
//コンストラクタ
public BirthdayCakeBlockEntity(BlockPos pPos, BlockState pState){
super(ExampleMod.BIRTHDAY_CAKE_ENTITY,pPos,pState);
}
//データをNBTに保存する処理
@Override
protected void saveAdditional(CompoundTag pTag) {
super.saveAdditional(pTag);
//ろうそくが立っているかの情報を保存
pTag.put("CandleLeft",candleLeft.save(new CompoundTag()));
pTag.put("CandleRight",candleRight.save(new CompoundTag()));
//ろうそくが着火されているかの情報を保存
pTag.putBoolean("LitLeft",this.litLeft);
pTag.putBoolean("LitRight",this.litRight);
pTag.putInt("EffectTicks", this.effectTicks);
}
//NBTからデータを読み込む処理
@Override
public void load(CompoundTag pTag) {
super.load(pTag);
this.candleLeft=ItemStack.of(pTag.getCompound("CandleLeft"));
this.candleRight=ItemStack.of(pTag.getCompound("CandleRight"));
this.litLeft = pTag.getBoolean("LitLeft");
this.litRight = pTag.getBoolean("LitRight");
this.effectTicks = pTag.getInt("EffectTicks");
}
@Override
public CompoundTag getUpdateTag() {
CompoundTag tag=new CompoundTag();
saveAdditional(tag);//現在のデータをNBTに保存
return tag;
}
@Override
public void handleUpdateTag(CompoundTag tag) {
load(tag);//受け取ったNBTでデータを読み込み
}
//tickメソッド
public static void tick(Level level, BlockPos pos, BlockState state, BirthdayCakeBlockEntity cakeEntity){
//エフェクトが実行中であれば何もしない
if(cakeEntity.effectTicks<=0){
return;
}
//カウンタを減らす
cakeEntity.effectTicks--;
//演奏する
if(level instanceof ServerLevel serverLevel){
cakeEntity.nextNoteTick--;
if(cakeEntity.nextNoteTick<=0&&cakeEntity.musicIndex<NOTE_PITCHES.length){
//次の音符を鳴らす時間をセットする
cakeEntity.nextNoteTick=NOTE_DELAYS[cakeEntity.musicIndex];
//音符ブロックの音を再生
float pitch=(float)Math.pow(2.0D,(double) (NOTE_PITCHES[cakeEntity.musicIndex]-12)/12.0D);
serverLevel.playSound(null,pos,SoundEvents.NOTE_BLOCK_BELL,SoundSource.RECORDS,5.0F,pitch);
serverLevel.sendParticles(
ParticleTypes.NOTE,
pos.getX()+0.5D,
pos.getY()+1.2D,
pos.getZ()+0.5D,
1,
(double) NOTE_PITCHES[cakeEntity.musicIndex]/24.0D,
0.0D,
0.0D,
1.0D
);
cakeEntity.musicIndex++;
}
}
//一定間隔で花火を出す
if(!level.isClientSide && cakeEntity.effectTicks%100==0){
ItemStack fireworkStack=new ItemStack(Items.FIREWORK_ROCKET);
CompoundTag fireworkTag=new CompoundTag();
ListTag explosionTag=new ListTag();
CompoundTag explosionDataTag=new CompoundTag();
explosionDataTag.putByte("Type",(byte) (level.random.nextInt(4)));
explosionDataTag.putIntArray("Colors",new int[]{level.random.nextInt(0xFFFFFF)});
explosionDataTag.putBoolean("Flicker",level.random.nextBoolean());
explosionDataTag.putBoolean("Trail",level.random.nextBoolean());
explosionTag.add(explosionDataTag);
fireworkTag.put("Explosions",explosionTag);
fireworkTag.putByte("Flight",(byte) (level.random.nextInt(2)+1));
fireworkStack.addTagElement("Fireworks",fireworkTag);
FireworkRocketEntity fireworkRocketEntity=new FireworkRocketEntity(
level,
pos.getX()+0.5D,
pos.getY()+1.2D,
pos.getZ()+0.5D,
fireworkStack
);
level.addFreshEntity(fireworkRocketEntity);
}
}
@Nullable
@Override
public Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
@Override
public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt) {
load(Objects.requireNonNull(pkt.getTag()));
}
//外部からろうそくの情報を取得・設定するためのメソッド
public ItemStack getCandleLeft(){
return candleLeft;
}
public ItemStack getCandleRight(){
return candleRight;
}
public void setCandleLeft(ItemStack stack){
this.candleLeft=stack.copy();
}
public void setCandleRight(ItemStack stack){
this.candleRight=stack.copy();
}
public boolean isLitLeft(){
return this.litLeft;
}
public boolean isLitRight(){
return this.litRight;
}
public void setLit(boolean left, boolean right){
boolean wasUnlit=!this.litLeft||!this.litRight;
this.litLeft=left;
this.litRight=right;
//両方のろうそくが着火された瞬間を検知
if(wasUnlit&&this.litLeft&&this.litRight){
startEffect();
}
if(level!=null&&!level.isClientSide) {
level.sendBlockUpdated(worldPosition,getBlockState(),getBlockState(),3);
setChanged();
}
}
private void startEffect(){
if(!level.isClientSide&&this.effectTicks<=0){
this.effectTicks=EFFECT_DURATION;
this.musicIndex=0;
this.nextNoteTick=0;
level.playSound(null,getBlockPos(), SoundEvents.PLAYER_LEVELUP,SoundSource.BLOCKS,1.0F,1.0F);
}
}
}
Rendererクラスの作成
ろうそくを置いたときの描画などを制御します。
package com.example.examplemod.birthdaycake;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import java.util.Objects;
public class BirthdayCakeRenderer implements BlockEntityRenderer<BirthdayCakeBlockEntity> {
public BirthdayCakeRenderer(BlockEntityRendererProvider.Context context){
}
@Override
public void render(BirthdayCakeBlockEntity cakeEntity, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) {
//BlockEntityから左右のろうそく情報を取得
ItemStack leftCandleStack=cakeEntity.getCandleLeft();
ItemStack rightCandleStack=cakeEntity.getCandleRight();
int skyLight=LightTexture.sky(packedLight);
int maxLight= LightTexture.pack(15,skyLight);
//左のろうそくを描画
if(!leftCandleStack.isEmpty()){
poseStack.pushPose();
poseStack.scale(0.8F,0.8F,0.8F);
poseStack.translate(0.85,1.25,0.55);
Minecraft.getInstance().getItemRenderer().renderStatic(
leftCandleStack,
ItemTransforms.TransformType.FIXED,
packedLight,
packedOverlay,
poseStack,
bufferSource,
0
);
poseStack.popPose();
}
//右のろうそく描画
if(!rightCandleStack.isEmpty()){
poseStack.pushPose();
poseStack.scale(0.8F,0.8F,0.8F);
poseStack.translate(0.35,1.25,0.55);
Minecraft.getInstance().getItemRenderer().renderStatic(
rightCandleStack,
ItemTransforms.TransformType.FIXED,
packedLight,
packedOverlay,
poseStack,
bufferSource,
0
);
poseStack.popPose();
}
if(cakeEntity.isLitLeft()){
addFlameParticle(Objects.requireNonNull(cakeEntity.getLevel()),0.7,1.55,0.45,cakeEntity.getBlockPos());
}
if(cakeEntity.isLitRight()){
addFlameParticle(Objects.requireNonNull(cakeEntity.getLevel()),0.3,1.55,0.45,cakeEntity.getBlockPos());
}
}
//パーティクル生成のためのメソッド
private void addFlameParticle(Level level, double xOffset, double yOffset, double zOffset, BlockPos pos){
if(level.random.nextFloat()<0.15F){
level.addParticle(ParticleTypes.FLAME,
pos.getX()+xOffset,
pos.getY()+yOffset,
pos.getZ()+zOffset,
0.0D,0.0D,0.0D);
}
}
}
BlockとBlockEntityとRendererをExampleModに登録
- Block
public static final Block BIRTHDAY_CAKE = new BirthdayCake().setRegistryName(MODID, "birthday_cake");
private static final RegisterBlockData[] registerBlocks = {
// ここにBlockを書いてね!
new RegisterBlockData(BIRTHDAY_CAKE)
};
- BlockEntity
public static final BlockEntityType<BirthdayCakeBlockEntity> BIRTHDAY_CAKE_ENTITY=
BlockEntityType.Builder
.of(BirthdayCakeBlockEntity::new,BIRTHDAY_CAKE)
.build(null);
@SubscribeEvent
public static void onBlockEntitiesRegistry(final RegistryEvent.Register<BlockEntityType<?>> event){
IForgeRegistry<BlockEntityType<?>> registry=event.getRegistry();
registry.register(BIRTHDAY_CAKE_ENTITY.setRegistryName(MODID,"birthday_cake_entity"));
}
- Renderer
private void doClientStuff(final FMLClientSetupEvent event) {
BlockEntityRenderers.register(
BIRTHDAY_CAKE_ENTITY,
BirthdayCakeRenderer::new
);
});
}
ろうそくアイテムを作成する
今回0~9までのろうそくアイテムを作成したのですが、設置の判定の際に冗長になるのを避けるために、
NumberCandleItemというクラスを作成し、そのクラスを継承するようにして数字のろうそくアイテムを作るようにしました。
- 親となるクラスNumberCandleItem
package com.example.examplemod.birthdaycake;
import net.minecraft.world.item.Item;
public class NumberCandleItem extends Item {
public NumberCandleItem(Properties properties){
super(properties);
}
}
あとは適当な名前をつけた10個のクラスをNumberCandleItemを継承するようにして作成します。
BirthdayCakeのjsonファイル
blockstates/birthday_cake.jsonについては以下のように、変化させたい分だけbitesパラメータで指定し、modelを割り当てます。
これに対応するように、models/blockにもjsonファイルを格納し、テクスチャを指定します。
{
"variants": {
"bites=0": {
"model": "examplemod:block/birthday_cake"
},
"bites=1": {
"model": "examplemod:block/birthday_cake_slice1"
},
"bites=2": {
"model": "examplemod:block/birthday_cake_slice2"
},
"bites=3": {
"model": "examplemod:block/birthday_cake_slice3"
},
"bites=4": {
"model": "examplemod:block/birthday_cake_slice4"
},
"bites=5": {
"model": "examplemod:block/birthday_cake_slice5"
}
}
}
ろうそくアイテムのjsonファイル
models/itemに格納するものです。以下の感じでパラメータを設定するといい感じになります。
{
"credit": "Made with Blockbench",
"texture_size": [32, 32],
"textures": {
"0": "examplemod:items/birthday_candle_0",
"particle": "examplemod:items/birthday_candle_0"
},
"elements": [
{
"from": [7, 0, 7],
"to": [9, 3, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 0, 7]},
"faces": {
"north": {"uv": [4, 7, 5, 8.5], "texture": "#0"},
"east": {"uv": [5, 7, 6, 8.5], "texture": "#0"},
"south": {"uv": [6, 7, 7, 8.5], "texture": "#0"},
"west": {"uv": [7, 7, 8, 8.5], "texture": "#0"},
"up": {"uv": [9, 1, 8, 0], "texture": "#0"},
"down": {"uv": [9, 1, 8, 2], "texture": "#0"}
}
},
{
"from": [6, 14, 7],
"to": [10, 16, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 5, 7]},
"faces": {
"north": {"uv": [2, 5, 4, 6], "texture": "#0"},
"east": {"uv": [2, 8, 3, 9], "texture": "#0"},
"south": {"uv": [4, 5, 6, 6], "texture": "#0"},
"west": {"uv": [8, 2, 9, 3], "texture": "#0"},
"up": {"uv": [8, 1, 6, 0], "texture": "#0"},
"down": {"uv": [8, 1, 6, 2], "texture": "#0"}
}
},
{
"from": [6, 3, 7],
"to": [10, 5, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [6, 3, 7]},
"faces": {
"north": {"uv": [2, 6, 4, 7], "texture": "#0"},
"east": {"uv": [3, 8, 4, 9], "texture": "#0"},
"south": {"uv": [6, 2, 8, 3], "texture": "#0"},
"west": {"uv": [8, 3, 9, 4], "texture": "#0"},
"up": {"uv": [8, 4, 6, 3], "texture": "#0"},
"down": {"uv": [6, 6, 4, 7], "texture": "#0"}
}
},
{
"from": [6, 5, 7],
"to": [10, 6.5, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [6, 5, 7]},
"faces": {
"north": {"uv": [6, 4, 8, 5], "texture": "#0"},
"east": {"uv": [8, 4, 9, 5], "texture": "#0"},
"south": {"uv": [6, 5, 8, 6], "texture": "#0"},
"west": {"uv": [8, 5, 9, 6], "texture": "#0"},
"up": {"uv": [8, 7, 6, 6], "texture": "#0"},
"down": {"uv": [4, 7, 2, 8], "texture": "#0"}
}
},
{
"from": [10, 5.5, 7],
"to": [11.5, 15.5, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [10, 6, 7]},
"faces": {
"north": {"uv": [0, 0, 1, 5], "texture": "#0"},
"east": {"uv": [1, 0, 2, 5], "texture": "#0"},
"south": {"uv": [2, 0, 3, 5], "texture": "#0"},
"west": {"uv": [3, 0, 4, 5], "texture": "#0"},
"up": {"uv": [9, 7, 8, 6], "texture": "#0"},
"down": {"uv": [9, 7, 8, 8], "texture": "#0"}
}
},
{
"from": [4.5, 5.5, 7],
"to": [6, 15.5, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 6, 7]},
"faces": {
"north": {"uv": [4, 0, 5, 5], "texture": "#0"},
"east": {"uv": [0, 5, 1, 10], "texture": "#0"},
"south": {"uv": [5, 0, 6, 5], "texture": "#0"},
"west": {"uv": [1, 5, 2, 10], "texture": "#0"},
"up": {"uv": [9, 9, 8, 8], "texture": "#0"},
"down": {"uv": [5, 8.5, 4, 9.5], "texture": "#0"}
}
},
{
"from": [7, 16, 7.5],
"to": [7, 18, 9.5],
"rotation": {"angle": 45, "axis": "y", "origin": [7, 16, 7]},
"faces": {
"north": {"uv": [0, 0, 0, 1], "texture": "#0"},
"east": {"uv": [5, 8.5, 6, 9.5], "texture": "#0"},
"south": {"uv": [0, 0, 0, 1], "texture": "#0"},
"west": {"uv": [6, 8.5, 7, 9.5], "texture": "#0"},
"up": {"uv": [0, 1, 0, 0], "texture": "#0"},
"down": {"uv": [0, 0, 0, 1], "texture": "#0"}
}
},
{
"from": [9, 16, 7.5],
"to": [9, 18, 9.5],
"rotation": {"angle": -45, "axis": "y", "origin": [9, 16, 7]},
"faces": {
"north": {"uv": [0, 0, 0, 1], "texture": "#0"},
"east": {"uv": [7, 8.5, 8, 9.5], "texture": "#0"},
"south": {"uv": [0, 0, 0, 1], "texture": "#0"},
"west": {"uv": [9, 0, 10, 1], "texture": "#0"},
"up": {"uv": [0, 1, 0, 0], "texture": "#0"},
"down": {"uv": [0, 0, 0, 1], "texture": "#0"}
}
}
],
"display": {
"firstperson_righthand": {
"rotation": [
0,
90,
0
],
"scale": [
0.7,
0.7,
0.7
]
},
"firstperson_lefthand": {
"rotation": [
0,
270,
0
],
"scale": [
0.7,
0.7,
0.7
]
},
"gui": {
"rotation": [
0,
180,
0
],
"scale": [
0.8,
0.8,
0.8
],
"translation": [
0,
-1,
0
]
},
"ground": {
"scale": [
0.7,
0.7,
0.7
]
},
"fixed": {
"scale": [
0.7,
0.7,
0.7
]
}
}
}
完成!!!!
