• Minecraft 1.19.2 Forge模组开发 03.动画生物实体


    1.12.2动画生物实体教程

    1.16.5动画生物实体教程

    1.18.2动画生物实体教程

    在这里插入图片描述

    效 果 展 示 效果展示

    我们本次尝试在1.19.2中添加一个能够具有各种动画效果动作的生物实体。

    1.首先,为了实现这些动画效果,我们需要首先使用到一个模组:geckolib(下载地址)

    找到项目的build.gradle文件,在repositoriesdependencies中添加依赖。

    repositories {
    
        //添加这个
        maven { url 'https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/' }
        
    }
    dependencies {
        minecraft 'net.minecraftforge:forge:1.19.2-43.1.1'
           
        //添加这个
        implementation fg.deobf('software.bernie.geckolib:geckolib-forge-1.19:3.1.36')
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    cr1.jpg

    之后点击Load Gradle按钮重新构建项目

    CR.jpg

    构建好了项目后在项目的Main类中添加一句geckolib的初始化语句:

    Main.java

        public Main()
        {
            IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
            // Register the commonSetup method for modloading
            bus.addListener(this::commonSetup);
            bus.addListener(this::setup);
            ItemInit.ITEMS.register(bus);
            BlockInit.BLOCKS.register(bus);
            EntityInit.ENTITY_TYPES.register(bus);
    
            
            //添加这个
            GeckoLib.initialize();
    
            MinecraftForge.EVENT_BUS.register(this);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.之后,与之前的教程一样,我们需要在blockbench中制作一个模组中的生物实体:

    进入软件后我们要找到一个插件按钮,然后再搜索栏中输入GeckoLib Animation Utils,并下载这个插件

    cr6.png

    将我们制作好的生物实体进行模型转换工作,找到Convert Project,之后选择Geckolib Animated Model

    cr7.png

    在这之后,你会发现你的生物实体栏多了一个Animate栏,点击进去:

    sam.jpg

    具体动作制作的视频:Blockbench动画制作

    在制作好所有的动画后我们导出模型和动画json文件。

    cc.png

    3.模型制作完成,接下来需要制作生物实体类,因为我们的生物的动作有很多,所以要在生物的不同状态时做出对应的动作。

    EntitySamca.java

    package com.joy187.re8joymod.entity;
    
    import java.util.EnumSet;
    
    import net.minecraft.sounds.SoundEvents;
    import net.minecraft.world.entity.EntityType;
    import net.minecraft.world.entity.LivingEntity;
    import net.minecraft.world.entity.Mob;
    import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
    import net.minecraft.world.entity.ai.attributes.Attributes;
    import net.minecraft.world.entity.ai.goal.FloatGoal;
    import net.minecraft.world.entity.ai.goal.Goal;
    import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
    import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
    import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
    import net.minecraft.world.entity.animal.IronGolem;
    import net.minecraft.world.entity.monster.Monster;
    import net.minecraft.world.entity.monster.Vex;
    import net.minecraft.world.entity.player.Player;
    import net.minecraft.world.entity.raid.Raider;
    import net.minecraft.world.level.Level;
    import net.minecraft.world.phys.Vec3;
    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 EntitySamca extends Vex implements IAnimatable{
    
    	private AnimationFactory factory = new AnimationFactory(this);
    
    	public EntitySamca(EntityType<? extends Vex> type, Level worldIn) {
            super(type, worldIn);
            this.xpReward = 20;
        }
    
    	public static AttributeSupplier.Builder prepareAttributes() {
    		return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 35.0D).
    				add(Attributes.ATTACK_DAMAGE, 8.5D).
    		    	add(Attributes.MOVEMENT_SPEED, 0.28D).
    		        add(Attributes.KNOCKBACK_RESISTANCE, 0.15D);
    	}
    
    	protected void registerGoals() {
    		      super.registerGoals();
    		      this.goalSelector.addGoal(0, new FloatGoal(this));
    		      this.goalSelector.addGoal(4, new ChargeAttackGoal());
    		      this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
    		      this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
    		      this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, Raider.class)).setAlertOthers());
    		      this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
    		      //this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, EntityEthan.class, true));
    		      this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
    
    	}
    	
        //我们生物的AI,决定使用哪种攻击方式
        class ChargeAttackGoal extends Goal {
            public ChargeAttackGoal() {
                this.setFlags(EnumSet.of(Flag.MOVE));
            }
    
            public boolean canUse() {
                if (EntitySamca.this.getTarget() != null && !EntitySamca.this.getMoveControl().hasWanted() && EntitySamca.this.random.nextInt(7) == 0) {
                    return EntitySamca.this.distanceToSqr(EntitySamca.this.getTarget()) > 4.0D;
                } else {
                    return false;
                }
            }
    
            public boolean canContinueToUse() {
                return EntitySamca.this.getMoveControl().hasWanted() && EntitySamca.this.isCharging() && EntitySamca.this.getTarget() != null && EntitySamca.this.getTarget().isAlive();
            }
    
            public void start() {
                LivingEntity livingentity = EntitySamca.this.getTarget();
                Vec3 vector3d = livingentity.getEyePosition(1.0F);
                EntitySamca.this.moveControl.setWantedPosition(vector3d.x, vector3d.y, vector3d.z, 1.0D);
                EntitySamca.this.setIsCharging(true);
                EntitySamca.this.playSound(SoundEvents.VEX_CHARGE, 1.0F, 1.0F);
            }
    
            public void stop() {
                EntitySamca.this.setIsCharging(false);
            }
    
            public void tick() {
                LivingEntity livingentity = EntitySamca.this.getTarget();
                if (EntitySamca.this.getBoundingBox().intersects(livingentity.getBoundingBox())) {
                    EntitySamca.this.doHurtTarget(livingentity);
                    EntitySamca.this.setIsCharging(false);
                } else {
                    double d0 = EntitySamca.this.distanceToSqr(livingentity);
                    if (d0 < 9.0D) {
                        Vec3 vector3d = livingentity.getEyePosition(1.0F);
                        EntitySamca.this.moveControl.setWantedPosition(vector3d.x, vector3d.y, vector3d.z, 1.0D);
                    }
                }
    
            }
        }
        
        //该状态播放器控制生物的各种动作
        private <E extends IAnimatable> PlayState predicate(AnimationEvent<E> event) {
    
            //找到了敌人,就播放攻击动画
        	if (this.isCharging()) {
                event.getController().setAnimation(new AnimationBuilder().playOnce("animation.samca.attack"));
                this.attackAnim=1;
                return PlayState.CONTINUE;
            }
            
            //平时就是飞行动画
            event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.samca.fly", true));
            return PlayState.CONTINUE;
        }
        
        //将我们之前的所有动画控制器进行注册
    	@Override
    	public void registerControllers(AnimationData data) {
            data.addAnimationController(new AnimationController(this, "controller",
                    0, this::predicate));
    	}
    
    	@Override
    	public AnimationFactory getFactory() {
    		return this.factory;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133

    4.新建生物实体模型文件ModelDund类

    ModelSamca.java

    package com.joy187.re8joymod.entity.model;
    
    import com.joy187.re8joymod.Main;
    import com.joy187.re8joymod.entity.EntitySamca;
    
    import net.minecraft.client.model.geom.ModelLayerLocation;
    import net.minecraft.resources.ResourceLocation;
    import software.bernie.geckolib3.model.AnimatedGeoModel;
    
    
    public class ModelSamca extends AnimatedGeoModel<EntitySamca>{
    	public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(new ResourceLocation(Main.MOD_ID, "samca"), "main");
    
        @Override
        public ResourceLocation getModelResource(EntitySamca object) {
            return new ResourceLocation(Main.MOD_ID, "geo/samca.geo.json");
        }
    
        @Override
        public ResourceLocation getTextureResource(EntitySamca object) {
            return new ResourceLocation(Main.MOD_ID, "textures/entity/dund2.png");
        }
    
        @Override
        public ResourceLocation getAnimationResource(EntitySamca animatable) {
            return new ResourceLocation(Main.MOD_ID, "animations/samca.animation.json");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    5.模型部分结束,开始着手渲染类的编写。新建RenderDund类。

    RenderDund.java

    package com.joy187.re8joymod.entity.render;
    
    import com.joy187.re8joymod.Main;
    import com.joy187.re8joymod.entity.EntitySamca;
    import com.joy187.re8joymod.entity.model.ModelSamca;
    import com.mojang.blaze3d.vertex.PoseStack;
    import com.mojang.blaze3d.vertex.VertexConsumer;
    
    import net.minecraft.client.renderer.MultiBufferSource;
    import net.minecraft.client.renderer.RenderType;
    import net.minecraft.client.renderer.entity.EntityRendererProvider;
    import net.minecraft.resources.ResourceLocation;
    import software.bernie.geckolib3.renderers.geo.GeoEntityRenderer;
    
    
    public class RenderSamca extends GeoEntityRenderer<EntitySamca>{
    
        public RenderSamca(EntityRendererProvider.Context renderManager) {
            super(renderManager, new ModelSamca());
            this.shadowRadius = 0.5f;
        }
    
        @Override
        public ResourceLocation getTextureLocation(EntitySamca instance) {
            //return LOCATION_BY_VARIANT.get(instance.getVariant());
        	return new ResourceLocation(Main.MOD_ID, "textures/entity/dund2.png");
        }
    
        @Override
        public RenderType getRenderType(EntitySamca animatable, float partialTicks, PoseStack stack,
                                        MultiBufferSource renderTypeBuffer, VertexConsumer vertexBuilder, int packedLightIn,
                                        ResourceLocation textureLocation) {
    //        if(animatable.isBaby()) {
    //            stack.scale(0.4F, 0.4F, 0.4F);
    //        } else {
    //            stack.scale(0.8F, 0.8F, 0.8F);
    //        }
        	stack.scale(1F, 1F, 1F);
    
            return super.getRenderType(animatable, partialTicks, stack, renderTypeBuffer, vertexBuilder, packedLightIn, textureLocation);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    6.在EntityInit.java中添加我们的生物信息:

    package com.joy187.re8joymod.init;
    
    import com.joy187.re8joymod.Main;
    import com.joy187.re8joymod.entity.EntitySamca;
    import net.minecraft.resources.ResourceLocation;
    import net.minecraft.world.entity.EntityType;
    import net.minecraft.world.entity.MobCategory;
    import net.minecraftforge.registries.DeferredRegister;
    import net.minecraftforge.registries.ForgeRegistries;
    import net.minecraftforge.registries.RegistryObject;
    
    public class EntityInit {
        public static final DeferredRegister<EntityType<?>> ENTITY_TYPES = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES,
                Main.MOD_ID);
    
        public static final RegistryObject<EntityType<EntitySamca>> SAMCA = ENTITY_TYPES.register("samca",
                () -> EntityType.Builder.of(EntitySamca::new, MobCategory.MONSTER).sized(1f,1.8f).setTrackingRange(20)
                        .build(new ResourceLocation(Main.MOD_ID, "samca").toString()));
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    ClientModEventSubscriber.java中添加我们的模型渲染、属性注册语句:
    @Mod.EventBusSubscriber(modid = Main.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
    public class ClientModEventSubscriber
    {
    
    
        @SubscribeEvent
        public static void onRegisterRenderer(EntityRenderersEvent.RegisterRenderers event) {
    		//添加渲染注册语句
            event.registerEntityRenderer(EntityInit.SAMCA.get(), RenderSamca::new);
            //event.registerEntityRenderer(EntityInit.DETONATOR_ENTITY.get(), RenderDetonatorEntity::new);
    
        }
        
        @SubscribeEvent
        public static void onAttributeCreate(EntityAttributeCreationEvent event) {
    		//添加属性注册语句
            event.put(EntityInit.SAMCA.get(), EntitySamca.prepareAttributes().build());
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    7.生物实体部分结束,接下来我们要给生物制作一个刷怪蛋:

    在ItemInit类中添加我们的刷怪蛋物品:
        public static final RegistryObject<Item> SAMCA_SPAWN_EGG = ITEMS.register("samca_spawn_egg",
                () -> new ForgeSpawnEggItem(EntityInit.SAMCA, 3093009, 4390912, new Item.Properties().tab(Main.TUTORIAL_TAB)));
    
    • 1
    • 2

    8.代码部分结束,来到资源包制作环节

    resources\assets\你的modid中的lang包中的en_us.json添加刷怪蛋和生物实体英文名称:

    	"item.re8joymod.samca_spawn_egg": "Mob Spawn Egg",
    	"entity.re8joymod.samca": "Mob",
    
    • 1
    • 2
    models\item包中添加刷怪蛋模型文件:

    samca_spawn_egg.json

    {
    	"parent": "item/template_spawn_egg"
    }
    
    • 1
    • 2
    • 3
    textures\entity中添加生物实体的皮肤贴图
    新建一个geo包和animation包,把第二步中的模型和动画文件分别放进去

    cr10.png

    9.最后我们还要给这个生物设定其掉落物

    resources\data\你的modid中新建loot_tables包-> loot_tables包中新建entities包 -> entities包中新建我们的战利品文件samaca.json

    crk.jpg
    战利品文件中参数含义
    name为物品名称,weight为掉落权重,越大掉这个物品的概率越高,count为掉落个数,min为最小掉几个,max为最大掉几个。

    samaca.json

    {
        "pools": [
            {
                "rolls": 1.0,
                "bonus_rolls": 0.0,
                "entries": [
                    {
                        "type": "item",
                        "name": "re8joymod:lei",
                        "weight": 5,
                        "functions": [
                            {
                                "function": "set_count",
                                "count": {
                                    "min": 1,
                                    "max": 3
                                }
                            },
                            {
                                "function": "looting_enchant",
                                "count": {
                                    "min": 0,
                                    "max": 1
                                }
                            }
                        ]
                    },
                    {
                        "type": "item",
                        "weight": 10,
                        "name": "minecraft:iron_nugget",
    					"functions": [
                           {
                                "function": "set_count",
                                "count": {
                                    "min": 2,
                                    "max": 3
                                }
                           }
                       ]
                    },
                    {
                        "type": "item",
                        "weight": 10,
                        "name": "minecraft:rotten_flesh",
    					"functions": [
                           {
                                "function": "set_count",
                                "count": {
                                    "min": 0,
                                    "max": 2
                                }
                           }
                       ]
                    },
                    {
                        "type": "item",
                        "weight": 5,
                        "name": "re8joymod:blacksheephead",
    					"functions": [
                           {
                                "function": "set_count",
                                "count": {
                                    "min": 0,
                                    "max": 2
                                }
                           }
                       ]
                    }
                 ]
            }
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    10.保存所有文件 -> 进行测试:

    fly.png

    起飞!

  • 相关阅读:
    在线打印资料的软件叫什么名字来着
    Geode多节点集群实验
    vue中全局修改elementui,message修改时长
    服务注册Eureka
    拔掉网线后, 原本的 TCP 连接还存在吗?
    【AD9361 数字接口CMOS &LVDS&SPI】B 并行数据之CMOS 续
    C语言文件操作——打开 &关闭 &顺序读写 &随机读写
    C++ Reference: Standard C++ Library reference: C Library: cwchar: btowc
    矩阵快速幂
    解锁Spring Boot的强大配置功能:@ConfigurationProperties与@PropertySources详解
  • 原文地址:https://blog.csdn.net/Jay_fearless/article/details/127807479