1.12.2动画生物实体教程
1.16.5动画生物实体教程
1.18.2动画生物实体教程
$$ 效果展示$$
我们本次尝试在1.19.2中添加一个能够做各种动作的生物实体。
1.首先,为了实现这些动画效果,我们需要首先使用到一个模组:geckolib(下载地址)
找到项目的build.gradle
文件,在repositories
和dependencies
中添加依赖。
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')
}
之后点击Load Gradle按钮重新构建项目
构建好了项目后在项目的Main类中添加一句geckolib的初始化语句,同时将EntityInit进行注册:
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类进行注册
EntityInit.ENTITY_TYPES.register(bus);
//添加这个
GeckoLib.initialize();
MinecraftForge.EVENT_BUS.register(this);
}
2.之后,与之前的教程一样,我们需要在blockbench中制作一个模组中的生物实体:
进入软件后我们要找到一个插件按钮,然后再搜索栏中输入GeckoLib Animation Utils
,并下载这个插件
将我们制作好的生物实体进行模型转换工作,找到Convert Project
,之后选择Geckolib Animated Model
在这之后,你会发现你的生物实体栏多了一个Animate栏
,点击进去:
具体动作制作的视频:Blockbench动画制作
在制作好所有的动画后我们导出模型和动画json文件。
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;
}
}
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");
}
}
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);
}
}
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()));
}
在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());
}
}
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)));
8.代码部分结束,来到资源包制作环节
在resources\assets\你的modid
中的lang包中的en_us.json
添加刷怪蛋和生物实体英文名称:
"item.re8joymod.samca_spawn_egg": "Mob Spawn Egg",
"entity.re8joymod.samca": "Mob",
在models\item
包中添加刷怪蛋模型文件:
samca_spawn_egg.json
{
"parent": "item/template_spawn_egg"
}
在textures\entity
中添加生物实体的皮肤贴图
新建一个geo包和animation包,把第二步中的模型和动画文件分别放进去
9.最后我们还要给这个生物设定其掉落物
在resources\data\你的modid
中新建loot_tables
包-> loot_tables
包中新建entities
包 -> entities
包中新建我们的战利品文件samaca.json
战利品文件中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
}
}
]
}
]
}
]
}
10.保存所有文件 -> 进行测试:
强
wc 这么牛 tql!!!