NeoForge-1.20.4Mod开发教程之第一个实体与数据同步

本篇教程参考为Flandre芙兰的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);
            });
        }
© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容