Minecraft 1.19.2 Fabric模组开发 04.动画效果方块
我们本次尝试在1.19 Fabric中制作一个具有动画效果的方块
效果演示
效果演示
效果演示
首先,请确保你的开发包中引入了geckolib依赖,相关教程请参考:Minecraft 1.19.2 Fabric模组开发 03.动画生物实体
1.首先我们要使用geckolib制作一个物品和对应的动画:
在blockbench中新建一个Geckolib动画模型
制作相关的模型和动画:
之后导出方块模型文件,动画文件,展示文件:
之后我们找到Geckolib Model Settings
并点击将模型转换为Block/Item
格式:
导出物品模型文件:
将这些文件分别放入到resources包中的如下位置:
2.在block包中新建一个我们的方块类BlockGooseBody:
BlockGooseBody.java
package net.joy187.joyggd.block;
import net.joy187.joyggd.init.BlockEntityInit;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.screen.NamedScreenHandlerFactory;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.DirectionProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.util.*;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import java.util.stream.Stream;
public class BlockGooseBody extends BlockWithEntity implements BlockEntityProvider {
//定义方块的朝向
public static final DirectionProperty FACING = Properties.HORIZONTAL_FACING;
public BlockGooseBody(Settings settings) {
super(settings);
}
//定义方块的碰撞体积
private static final VoxelShape SHAPE = Stream.of(
Block.createCuboidShape(3.0D, 0.0D, 3.0D, 13.0D, 13.0D, 13.0D)
).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
//确定与之绑定的方块实体
@Nullable
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new BlockEntityGooseBody(pos, state);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return SHAPE;
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return this.getDefaultState().with(FACING, ctx.getPlayerFacing().getOpposite());
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.rotate(mirror.getRotation(state.get(FACING)));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(FACING);
}
@Override
public BlockRenderType getRenderType(BlockState state) {
return BlockRenderType.ENTITYBLOCK_ANIMATED;
}
}
在BlockInit类中将我们的物品进行注册:
BlockInit.java
package net.joy187.joyggd.init;
import net.minecraft.block.Block;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.joy187.joyggd.ModMain;
import net.joy187.joyggd.block.BlockGooseBody;
import net.minecraft.block.*;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.intprovider.UniformIntProvider;
import net.minecraft.util.registry.Registry;
public class BlockInit {
//注册该方块
public static final Block GOOSEBODY = registerBlockWithoutBlockItem("goosebody" ,
new BlockGooseBody(FabricBlockSettings.copy(Blocks.BONE_BLOCK).nonOpaque()));
private static Block registerBlockWithoutBlockItem(String name, Block block) {
return Registry.register(Registry.BLOCK, new Identifier(ModMain.MOD_ID, name), block);
}
private static Block registerBlock(String name, Block block, ItemGroup group) {
registerBlockItem(name, block, group);
return Registry.register(Registry.BLOCK, new Identifier(ModMain.MOD_ID, name), block);
}
private static Item registerBlockItem(String name, Block block, ItemGroup group) {
return Registry.register(Registry.ITEM, new Identifier(ModMain.MOD_ID, name),
new BlockItem(block, new FabricItemSettings().group(group)));
}
public static void registerModBlocks() {
System.out.println("Registering Mod Blocks for " + ModMain.MOD_ID);
}
}
3.在block包新建一个方块实体类BlockEntityGooseBody:
BlockEntityGooseBody.java
package net.joy187.joyggd.block;
import net.joy187.joyggd.entity.EntityGoose;
import net.joy187.joyggd.init.BlockEntityInit;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.util.math.BlockPos;
import software.bernie.geckolib3.core.IAnimatable;
import software.bernie.geckolib3.core.PlayState;
import software.bernie.geckolib3.core.builder.AnimationBuilder;
import software.bernie.geckolib3.core.controller.AnimationController;
import software.bernie.geckolib3.core.event.predicate.AnimationEvent;
import software.bernie.geckolib3.core.manager.AnimationData;
import software.bernie.geckolib3.core.manager.AnimationFactory;
public class BlockEntityGooseBody extends BlockEntity implements IAnimatable {
private AnimationFactory manager = new AnimationFactory(this);
public BlockEntityGooseBody(BlockPos pos, BlockState state) {
super(BlockEntityInit.BLOCKGOOSEBODY , pos, state);
// TODO Auto-generated constructor stub
}
//指定该状态机播放什么动画
private <E extends IAnimatable> PlayState predicate(AnimationEvent<E> event) {
event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.goosebody.rotate", true));
return PlayState.CONTINUE;
}
//将状态机进行注册
@Override
public void registerControllers(AnimationData data) {
data.addAnimationController(new AnimationController<BlockEntityGooseBody>(this, "controller",
0, this::predicate));
}
@Override
public AnimationFactory getFactory() {
// TODO Auto-generated method stub
return this.manager;
}
}
新建一个BlockEntityInit
类将该方块实体进行注册:
BlockEntityInit.java
package net.joy187.joyggd.init;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.joy187.joyggd.ModMain;
import net.joy187.joyggd.block.BlockEntityGooseBody;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class BlockEntityInit {
public static BlockEntityType<BlockEntityGooseBody> BLOCKGOOSEBODY;
public static void registerAllBlockEntities() {
//将该方块实体进行注册
BLOCKGOOSEBODY = Registry.register(Registry.BLOCK_ENTITY_TYPE,
new Identifier(ModMain.MOD_ID, "goosebody"),FabricBlockEntityTypeBuilder.create(BlockEntityGooseBody::new
,BlockInit.GOOSEBODY).build(null));
}
}
4.之后就是该实体的渲染工作了,新建一个模型类ModelGooseBody
和一个渲染类RenderGooseBody
ModelGooseBody.java
package net.joy187.joyggd.block.model;
import net.joy187.joyggd.ModMain;
import net.joy187.joyggd.block.BlockEntityGooseBody;
import net.joy187.joyggd.entity.EntityGoose;
import net.minecraft.util.Identifier;
import software.bernie.geckolib3.model.AnimatedGeoModel;
public class ModelGooseBody extends AnimatedGeoModel<BlockEntityGooseBody> {
//方块模型文件地址
@Override
public Identifier getModelResource(BlockEntityGooseBody animatable) {
return new Identifier(ModMain.MOD_ID, "geo/goosebody.geo.json");
}
//方块贴图文件地址
@Override
public Identifier getTextureResource(BlockEntityGooseBody entity) {
return new Identifier(ModMain.MOD_ID, "textures/block/body.png");
}
//方块动画文件地址
@Override
public Identifier getAnimationResource(BlockEntityGooseBody entity) {
return new Identifier(ModMain.MOD_ID, "animations/goosebody.animation.json");
}
}
这个类用来将我们刚刚的模型进行渲染
RenderGooseBody.java
package net.joy187.joyggd.block.render;
import net.joy187.joyggd.ModMain;
import net.joy187.joyggd.block.BlockEntityGooseBody;
import net.joy187.joyggd.block.model.ModelGooseBody;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import software.bernie.geckolib3.renderers.geo.GeoBlockRenderer;
public class RenderGooseBody extends GeoBlockRenderer<BlockEntityGooseBody> {
public RenderGooseBody(BlockEntityRendererFactory.Context context) {
//将上面的模型传进来
super(new ModelGooseBody());
}
@Override
public RenderLayer getRenderType(BlockEntityGooseBody animatable, float partialTicks, MatrixStack stack,
VertexConsumerProvider renderTypeBuffer, VertexConsumer vertexBuilder,
int packedLightIn, Identifier textureLocation) {
return RenderLayer.getEntityTranslucent(getTextureResource(animatable));
}
}
5.方块制作完了,需要有一个物品与之对应,所以在Items包中新建一个物品类ItemGooseBody:
ItemGooseBody.java
package net.joy187.joyggd.items;
import net.minecraft.block.Block;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import software.bernie.geckolib3.core.IAnimatable;
import software.bernie.geckolib3.core.PlayState;
import software.bernie.geckolib3.core.controller.AnimationController;
import software.bernie.geckolib3.core.event.predicate.AnimationEvent;
import software.bernie.geckolib3.core.manager.AnimationData;
import software.bernie.geckolib3.core.manager.AnimationFactory;
public class ItemGooseBody extends BlockItem implements IAnimatable{
public AnimationFactory factory = new AnimationFactory(this);
public ItemGooseBody(Block block,Settings settings){
super(block,settings);
}
//指定该状态机播放什么动画
private <E extends IAnimatable> PlayState predicate(AnimationEvent<E> event){
//event.getController().setAnimation(new AnimationBuilder().addAnimation("idle" ,stoudloop trwe));
return PlayState.CONTINUE;
}
//注册所有状态机
@Override
public void registerControllers(AnimationData animationData){
animationData.addAnimationController(new AnimationController(this,"controller",
0, this::predicate));
}
@Override
public AnimationFactory getFactory()
{
return this.factory;
}
}
之后我们要对该方块进行建模和渲染,分别新建一个模型类ModelItemGooseBody和渲染类RenderItemGooseBody:
ModelItemGooseBody.java
package net.joy187.joyggd.items.model;
import net.joy187.joyggd.ModMain;
import net.joy187.joyggd.items.ItemGooseBody;
import net.minecraft.util.Identifier;
import software.bernie.geckolib3.model.AnimatedGeoModel;
//将上面的物品类填入<>中
public class ModelItemGooseBody extends AnimatedGeoModel<ItemGooseBody> {
//模型地址
@Override
public Identifier getModelResource(ItemGooseBody animatable) {
return new Identifier(ModMain.MOD_ID, "geo/goosebody.geo.json");
}
//贴图地址
@Override
public Identifier getTextureResource(ItemGooseBody entity) {
return new Identifier(ModMain.MOD_ID, "textures/block/body.png");
}
//动画文件地址
@Override
public Identifier getAnimationResource(ItemGooseBody entity) {
return new Identifier(ModMain.MOD_ID, "animations/goosebody.animation.json");
}
}
之后是渲染文件,用于将上面的物品模型进行渲染:
RenderItemGooseBody.java
package net.joy187.joyggd.items.render;
import net.joy187.joyggd.items.ItemGooseBody;
import net.joy187.joyggd.items.model.ModelItemGooseBody;
import software.bernie.geckolib3.renderers.geo.GeoItemRenderer;
//将上面的物品类填入<>中
public class RenderItemGooseBody extends GeoItemRenderer<ItemGooseBody> {
public RenderItemGooseBody() {
//将上面的物品模型文件放入super()中
super(new ModelItemGooseBody());
}
}
在ItemInit中注册我们的方块:
ItemInit.java
package net.joy187.joyggd.init;
import net.minecraft.item.Item;
import net.minecraft.item.SpawnEggItem;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.joy187.joyggd.ModMain;
import net.joy187.joyggd.items.ItemGooseBody;
import net.joy187.joyggd.items.ItemPele;
import net.joy187.joyggd.items.ItemSheriff;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class ItemInit {
//注册该物品
public static final Item GOOSEBODY = registerItem("goosebody",
//这个是指定该物品与BlockInit中的一个方块形成对应关系
new ItemGooseBody(BlockInit.GOOSEBODY,new FabricItemSettings().group(ModMain.ITEMTAB)));
private static Item registerItem(String name, Item item) {
return Registry.register(Registry.ITEM, new Identifier(ModMain.MOD_ID, name), item);
}
public static void registerModItems() {
ModMain.LOGGER.debug("Registering Mod Items for " + ModMain.MOD_ID);
}
}
6.在项目主类中添加相关类注册事件:
ModMain.java
package net.joy187.joyggd;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
import net.joy187.joyggd.entity.EntityGoose;
import net.joy187.joyggd.init.BlockEntityInit;
import net.joy187.joyggd.init.BlockInit;
import net.joy187.joyggd.init.EntityInit;
import net.joy187.joyggd.init.ItemInit;
import net.minecraft.client.render.entity.FlyingItemEntityRenderer;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import software.bernie.geckolib3.GeckoLib;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ModMain implements ModInitializer {
public static final String MOD_ID = "joyggd";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static final ItemGroup ITEMTAB = FabricItemGroupBuilder.build(
new Identifier(MOD_ID, "itemtab"), () -> new ItemStack(ItemInit.SHERIFF));
@Override
public void onInitialize() {
//物品总类注册
ItemInit.registerModItems();
//方块类BlockInit注册
BlockInit.registerModBlocks();
//方块实体类注册
BlockEntityInit.registerAllBlockEntities();
GeckoLib.initialize();
}
}
来到客户端类,将一系列方块和实体的渲染进行注册:
ModClient.java
package net.joy187.joyggd;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
import net.joy187.joyggd.block.render.RenderGooseBody;
import net.joy187.joyggd.entity.render.RenderGoose;
import net.joy187.joyggd.init.BlockEntityInit;
import net.joy187.joyggd.init.BlockInit;
import net.joy187.joyggd.init.EntityInit;
import net.joy187.joyggd.init.ItemInit;
import net.joy187.joyggd.items.render.RenderItemGooseBody;
import net.minecraft.client.render.RenderLayer;
import software.bernie.geckolib3.renderers.geo.GeoItemRenderer;
public class ModClient implements ClientModInitializer{
@Override
public void onInitializeClient() {
//方块渲染类型注册
BlockRenderLayerMap.INSTANCE.putBlock(BlockInit.GOOSEBODY, RenderLayer.getCutout());
//方块实体渲染进行注册
BlockEntityRendererRegistry.register(BlockEntityInit.BLOCKGOOSEBODY, RenderGooseBody::new);
//物品渲染类型进行注册
GeoItemRenderer.registerItemRenderer(ItemInit.GOOSEBODY, new RenderItemGooseBody());
}
}
7.代码部分结束,来到资源包制作。
在lang语言包的en_us.json中添加方块的名称:
en_us.json
"block.joyggd.goosebody":"Block Name"
在blockstates中添加方块状态文件:
goosebody.json
{
"variants":{
"facing=north":{"model":"joyggd:block/goosebody"},
"facing=east":{"model":"joyggd:block/goosebody","y":90},
"facing=south":{"model":"joyggd:block/goosebody","y":180},
"facing=west":{"model":"joyggd:block/goosebody","y":270}
}
}
来到数据包中,在data\你的modid\loot_tables\blocks
中创建一个方块挖掉后的掉落物文件:
goosebody.json
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "joyggd:goosebody"
}
]
}
]
}
8.进入游戏调试。
第一步中导出的文件源代码:
方块的geo文件(放入geo文件夹):
goosebody.geo.json
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.goosebody - Converted",
"texture_width": 16,
"texture_height": 16,
"visible_bounds_width": 2,
"visible_bounds_height": 2.5,
"visible_bounds_offset": [0, 0.75, 0]
},
"bones": [
{
{
"origin": [-4.33043, 0, -5.47826],
"size": [0.17391, 0.08696, 3.92609],
"uv": {
"north": {"uv": [2.66957, 14.8913], "uv_size": [0.17391, -0.51304]},
"east": {"uv": [2.86957, 3.65217], "uv_size": [0.08696, 11.82609]},
"south": {"uv": [3.04348, 3.65217], "uv_size": [-0.17391, 0.08696]},
"west": {"uv": [2.95652, 3.65217], "uv_size": [0.08696, 11.82609]},
"up": {"uv": [3.73043, 14.52174], "uv_size": [-0.17391, -0.51304]},
"down": {"uv": [3.26848, 14.27717], "uv_size": [-0.17391, 0.32609]}
}
},
//略
{
"origin": [1.06087, 0, -5.47826],
"size": [0.34783, 0.08696, 6.88261],
"uv": {
"north": {"uv": [8.06087, 14.8913], "uv_size": [0.34783, -0.51304]},
"east": {"uv": [8.26087, 0.69565], "uv_size": [0.08696, 14.78261]},
"south": {"uv": [8.6087, 0.69565], "uv_size": [-0.34783, 0.08696]},
"west": {"uv": [8.52174, 0.69565], "uv_size": [0.08696, 14.78261]},
"up": {"uv": [9.29565, 14.52174], "uv_size": [-0.34783, -3.46957]},
"down": {"uv": [8.8337, 11.32065], "uv_size": [-0.34783, 3.28261]}
}
}
]
}
]
}
]
}
本次方块的动画文件(放入animations文件夹):
goosebody.animations.json
{
"format_version": "1.8.0",
"animations": {
"animation.goosebody.rotate": {
"loop": true,
"animation_length": 2,
"bones": {
"body": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.5": {
"vector": [0, -90, 0]
},
"1.0": {
"vector": [0, -180, 0]
},
"1.5": {
"vector": [0, -270, 0]
},
"2.0": {
"vector": [0, -360, 0]
}
}
}
}
}
},
"geckolib_format_version": 2
}
方块展示文件(放入models/block文件夹):
goosebody.json
{
"credit": "Made with Blockbench",
"parent": "builtin/entity",
"display": {
"thirdperson_righthand": {
"scale": [
0.75,
0.75,
0.75
]
},
"thirdperson_lefthand": {
"scale": [
0.75,
0.75,
0.75
]
},
"firstperson_righthand": {
"rotation": [
-180,
-3.25,
-180
],
"translation": [
0,
-1.5,
-3
],
"scale": [
0.75,
0.75,
0.75
]
},
"firstperson_lefthand": {
"rotation": [
-180,
32,
180
],
"translation": [
0.75,
-1.75,
-1.75
],
"scale": [
0.75,
0.75,
0.75
]
},
"ground": {
"translation": [
0,
-3.5,
0
]
},
"gui": {
"translation": [
0,
-7.5,
0
]
},
"head": {
"translation": [
0,
6.25,
0
]
},
"fixed": {
"translation": [
-0.75,
-5.5,
0
]
}
}
}
物品模型文件(放入models/item文件夹):
goosebody.json
{
略
}