跳到主要内容

28自定义怪物

建模使用blockbench,安装插件:

  • Animated Java

代码

新建一个entity包,用来存放我们自己写的生物,本次要写的豪猪完整结构如下:

image-20240825130655294

ModEntities:

public class ModEntities {
public static final EntityType<PorcupineEntity> PORCUPINE = Registry.register(Registries.ENTITY_TYPE,
new Identifier(TutorialMod.MOD_ID, "porcupine"),
FabricEntityTypeBuilder.create(SpawnGroup.CREATURE, PorcupineEntity::new)
.dimensions(EntityDimensions.fixed(1f, 1f)).build());

}

PorcupineEntity:

public class PorcupineEntity extends AnimalEntity {
public final AnimationState idleAnimationState = new AnimationState();
private int idleAnimationTimeout = 0;

public PorcupineEntity(EntityType<? extends AnimalEntity> entityType, World world) {
super(entityType, world);
}

private void setupAnimationStates() {
if (this.idleAnimationTimeout <= 0) {
this.idleAnimationTimeout = this.random.nextInt(40) + 80;
this.idleAnimationState.start(this.age);
} else {
--this.idleAnimationTimeout;
}
}

@Override
protected void updateLimbs(float posDelta) {
float f = this.getPose() == EntityPose.STANDING ? Math.min(posDelta * 6.0f, 1.0f) : 0.0f;
this.limbAnimator.updateLimbs(f, 0.2f);
}

@Override
public void tick() {
super.tick();
if(this.getWorld().isClient()) {
setupAnimationStates();
}
}

@Override
protected void initGoals() {
this.goalSelector.add(0, new SwimGoal(this));

this.goalSelector.add(1, new AnimalMateGoal(this, 1.15D));
this.goalSelector.add(2, new TemptGoal(this, 1.25D, Ingredient.ofItems(Items.BEETROOT), false));

this.goalSelector.add(3, new FollowParentGoal(this, 1.15D));

this.goalSelector.add(4, new WanderAroundFarGoal(this, 1D));
this.goalSelector.add(5, new LookAtEntityGoal(this, PlayerEntity.class, 4f));
this.goalSelector.add(6, new LookAroundGoal(this));
}

public static DefaultAttributeContainer.Builder createPorcupineAttributes() {
return MobEntity.createMobAttributes()
.add(EntityAttributes.GENERIC_MAX_HEALTH, 15)
.add(EntityAttributes.GENERIC_MOVEMENT_SPEED, 0.2f)
.add(EntityAttributes.GENERIC_ARMOR, 0.5f)
.add(EntityAttributes.GENERIC_ATTACK_DAMAGE, 2);
}

@Override
public boolean isBreedingItem(ItemStack stack) {
return stack.isOf(Items.BEETROOT);
}

@Nullable
@Override
public PassiveEntity createChild(ServerWorld world, PassiveEntity entity) {
return ModEntities.PORCUPINE.create(world);
}

@Nullable
@Override
protected SoundEvent getAmbientSound() {
return SoundEvents.ENTITY_FOX_AMBIENT;
}

@Nullable
@Override
protected SoundEvent getHurtSound(DamageSource source) {
return SoundEvents.ENTITY_CAT_HURT;
}

@Nullable
@Override
protected SoundEvent getDeathSound() {
return SoundEvents.ENTITY_DOLPHIN_DEATH;
}
}

ModModelLayers:

public class ModModelLayers {
public static final EntityModelLayer PORCUPINE =
new EntityModelLayer(new Identifier(TutorialMod.MOD_ID, "porcupine"), "main");
}

PorcupineRenderer:

public class PorcupineRenderer extends MobEntityRenderer<PorcupineEntity, PorcupineModel<PorcupineEntity>> {
private static final Identifier TEXTURE = new Identifier(TutorialMod.MOD_ID, "textures/entity/porcupine.png");

public PorcupineRenderer(EntityRendererFactory.Context context) {
super(context, new PorcupineModel<>(context.getPart(ModModelLayers.PORCUPINE)), 0.6f);
}

@Override
public Identifier getTexture(PorcupineEntity entity) {
return TEXTURE;
}

@Override
public void render(PorcupineEntity mobEntity, float f, float g, MatrixStack matrixStack,
VertexConsumerProvider vertexConsumerProvider, int i) {
if(mobEntity.isBaby()) {
matrixStack.scale(0.5f, 0.5f, 0.5f);
} else {
matrixStack.scale(1f, 1f, 1f);
}

super.render(mobEntity, f, g, matrixStack, vertexConsumerProvider, i);
}
}

PorcupineModel是通过blockbench生成的,

public class PorcupineModel<T extends PorcupineEntity> extends SinglePartEntityModel<T> {
private final ModelPart porcupine;
private final ModelPart head;

public PorcupineModel(ModelPart root) {
this.porcupine = root.getChild("porcupine");
this.head = porcupine.getChild("body").getChild("torso").getChild("head");
}

public static TexturedModelData getTexturedModelData() {
xxx
}

@Override
public void setAngles(PorcupineEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) {
this.getPart().traverse().forEach(ModelPart::resetTransform);
this.setHeadAngles(netHeadYaw, headPitch);

this.animateMovement(ModAnimations.PORCUPINE_WALK, limbSwing, limbSwingAmount, 2f, 2.5f);
this.updateAnimation(entity.idleAnimationState, ModAnimations.PORCUPINE_IDLE, ageInTicks, 1f);
}

private void setHeadAngles(float headYaw, float headPitch) {
headYaw = MathHelper.clamp(headYaw, -30.0F, 30.0F);
headPitch = MathHelper.clamp(headPitch, -25.0F, 45.0F);

this.head.yaw = headYaw * 0.017453292F;
this.head.pitch = headPitch * 0.017453292F;
}

@Override
public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) {
porcupine.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
}

@Override
public ModelPart getPart() {
return porcupine;
}
}

ModAnimations用来描述动画,也是生成的

最后在ModItems中注册生物蛋

public static final Item PORCUPINE_SPAWN_EGG = registerItem("porcupine_spawn_egg",
new SpawnEggItem(ModEntities.PORCUPINE, 0xa86518, 0x3b260f, new FabricItemSettings()));

在ModItemGroups添加到物品组:

entries.add(ModItems.PORCUPINE_SPAWN_EGG);

在ModModelProvider中:

itemModelGenerator.register(ModItems.PORCUPINE_SPAWN_EGG,
new Model(Optional.of(new Identifier("item/template_spawn_egg")), Optional.empty()));

初始化的时候进行加载TutorialMod:

        FuelRegistry.INSTANCE.add(ModItems.COAL_BRIQUETTE, 200);
FabricDefaultAttributeRegistry.register(ModEntities.PORCUPINE, PorcupineEntity.createPorcupineAttributes());

TutorialModClient:

EntityRendererRegistry.register(ModEntities.PORCUPINE, PorcupineRenderer::new);
EntityModelLayerRegistry.registerModelLayer(ModModelLayers.PORCUPINE, PorcupineModel::getTexturedModelData);

效果

生成一个怪物:

/summon tutorialmod:porcupine

交配:

image-20240825125334800