本篇教程参考为的NeoForge1.20.4Mod开发的视频,这篇教程是我边学边总结的,与其说是教程更像我的学习笔记,如有不清楚的地方,大家可以在评论区提问。在我力所能及的范围内,我都会解答的。
构建一个实体我们需要以下这些东西:
-
实体类,直接或者间接的继承
Entity类,这个类决定了实体的是如何工作的 -
渲染类,直接或者间接的继承
EntityRenderer<T extends Entity>,这个类决定了实体是如何渲染的 -
模型类,直接或者间接的继承
EntityModel<T extends Entity>,这个类规定的实体的模型是什么
我们先从最核心的实体类开始
创建FlyingSwordEntity类
public class FlyingSwordEntity extends Entity {
private static final Logger LOGGER = LogUtils.getLogger();
//用于同步的字段
private static final EntityDataAccessor<Integer> COUNTER = SynchedEntityData.defineId(FlyingSwordEntity.class, EntityDataSerializers.INT);
@Override
public void tick() {
if(this.level().isClientSide){
//读取COUNTER并打印
Integer i = this.entityData.get(COUNTER);
LOGGER.info(i.toString());
}
if(!this.level().isClientSide){
//打印并使COUNTER加1
LOGGER.info(this.entityData.get(COUNTER).toString());
this.entityData.set(COUNTER,this.entityData.get(COUNTER)+1);
}
super.tick();
}
public FlyingSwordEntity(EntityType<?> pEntityType, Level pLevel) {
super(pEntityType, pLevel);
}
@Override
protected void defineSynchedData() {//注册该实体所有“自动同步”的 entityData 字段和默认值
this.entityData.define(COUNTER,0);
}
@Override
protected void readAdditionalSaveData(CompoundTag pCompound) {//从NBT读取额外数据
this.entityData.set(COUNTER,pCompound.getInt("counter"));
}
@Override
protected void addAdditionalSaveData(CompoundTag pCompound) {//把额外数据写入NBT
pCompound.putInt("counter",this.entityData.get(COUNTER));
}
}
创建FlyingSwordEntityRenderer类
public class FlyingSwordEntityRenderer extends EntityRenderer {
private EntityModel<FlyingSwordEntity> model;
public FlyingSwordEntityRenderer(EntityRendererProvider.Context context) {
super(context);
//bakeLayer(...):把已注册的LayerDefinition烘焙成ModelPart
model = new FlyingSwordModel(context.bakeLayer(FlyingSwordModel.LAYER_LOCATION));
}
@Override
public ResourceLocation getTextureLocation(Entity entity) {
return new ResourceLocation(ExampleMod.MODID, "textures/entity/flying_sword_entity.png");
}
@Override
public void render(Entity entity, float entityYaw, float partialTick, PoseStack matrixStack, MultiBufferSource buffer, int packedLightIn) {
super.render(entity,entityYaw,partialTick,matrixStack,buffer,packedLightIn);
matrixStack.pushPose();
matrixStack.mulPose(Axis.YN.rotationDegrees(45));
matrixStack.translate(0.0D, -1.0D, 0.0D);
VertexConsumer vertexConsumer = buffer.getBuffer(this.model.renderType(this.getTextureLocation(entity)));
this.model.renderToBuffer(matrixStack,vertexConsumer,packedLightIn, OverlayTexture.NO_OVERLAY,1,1,1,1);
matrixStack.popPose();
}
}
在Minecraft/NeoForge模型系统里:
LayerDefinition是模型“蓝图” 描述模型由哪些部件组成、每个部件的方块几何(addBox)、贴图尺寸(比如 64×64)
通常在createBodyLayer() 里构建并返回
它还不是可直接渲染的对象,更像“设计图”
ModelPart是模型“可渲染实例”,是把 LayerDefinition 经过 bakeLayer(...) 烘焙后得到的运行时对象
你会在模型构造器里拿到根 ModelPart root,再 root.getChild("bb_main") 取子部件
可以直接改旋转/位移(如 xRot/yRot/zRot),并在 renderToBuffer 里真正绘制
LayerDefinition 负责“定义长什么样”,ModelPart 负责“在游戏里实际动起来并画出来”
创建FlyingSwordModel类
这个类由Blockbench生成,只需改动部分地方
public class FlyingSwordModel extends EntityModel<FlyingSwordEntity> {
//更改模组id和实体名称
public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(new ResourceLocation(ExampleMod.MODID,"flying_sword_entity"), "main");
private final ModelPart bb_main;
public FlyingSwordModel(ModelPart root) {
this.bb_main = root.getChild("bb_main");
}
public static LayerDefinition createBodyLayer() {
MeshDefinition meshdefinition = new MeshDefinition();
PartDefinition partdefinition = meshdefinition.getRoot();
PartDefinition bb_main = partdefinition.addOrReplaceChild("bb_main", CubeListBuilder.create().texOffs(0, 19).addBox(-5.0F, -1.0F, -5.0F, 10.0F, 1.0F, 1.0F, new CubeDeformation(0.01F))
.texOffs(0, 0).addBox(-1.0F, -1.0F, -9.0F, 2.0F, 1.0F, 18.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 24.0F, 0.0F));
return LayerDefinition.create(meshdefinition, 64, 64);
}
@Override
public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) {
bb_main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha);
}
//渲染器不调用不生效,用于控制每帧模型姿态
@Override
public void setupAnim(FlyingSwordEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) {
bb_main.xRot = limbSwing;
bb_main.yRot = netHeadYaw;
bb_main.zRot = headPitch;
}
}
各类的注册
实体的注册
public class ModEntityTypes {
public static final DeferredRegister<EntityType<?>> ENTITY_TYPES=DeferredRegister.create(Registries.ENTITY_TYPE, ExampleMod.MODID);
public static final Supplier<EntityType<FlyingSwordEntity>> FLYING_SWORD_ENTITY=ENTITY_TYPES.register("flying_sword_entity",
()->EntityType.Builder.of(FlyingSwordEntity::new, MobCategory.MISC)
.sized(1f,1f)//碰撞箱大小
.build("'flying_sword_entity'"));
public static void register(IEventBus bus) {
ENTITY_TYPES.register(bus);
}
}
模型的注册
写在ClientEventHandler类中
@SubscribeEvent
public static void registerEntityLayers(EntityRenderersEvent.RegisterLayerDefinitions event) {
event.registerLayerDefinition(FlyingSwordModel.LAYER_LOCATION,FlyingSwordModel::createBodyLayer);
}
渲染器的注册
写在ClientEventHandler类中
@SubscribeEvent
public static void onClientEvent(FMLClientSetupEvent event){
event.enqueueWork(() -> {
BlockEntityRenderers.register(ModBlockEntities.RUBY_FRAME_BLOCK_ENTITY.get(),RubyFrameBlockEntityRender::new);
EntityRenderers.register(ModEntityTypes.FLYING_SWORD_ENTITY.get(), FlyingSwordEntityRenderer::new);
});
}









暂无评论内容