User Tools

Site Tools


zh_cn:tutorial:entity

创建一个实体

本文的源码托管于 这个仓库 的 entity 分支.

实体(Entity)是游戏世界里的一种可以根据附加的逻辑移动的物体,包括:

  • 矿车

生物实体(Living Entity)是拥有生命值,并且可以造成伤害的实体。 为了实现不同的功能,生物实体有着不同的分支类型,其中有:

  • HostileEntity 敌对实体,用于僵尸、苦力怕、骷髅等
  • AnimalEntity 动物实体,用于羊、牛、猪等
  • WaterCreatureEntity 水生物实体,用于可以游泳的实体
  • FishEntity 鱼实体,用于鱼

具体继承、使用哪个取决于你的需求和目的。 随着开发进度的推进,对于特定的任务,实体的逻辑变得越来越具体、简明。有以下两种继承自 LivingEntity 的实体:

  • MobEntity
  • PathAwareEntity

MobEntity 具有AI逻辑和移动控制。 PathAwareEntity 提供额外的寻路系统,很多AI任务都需要用到寻路。

在这篇教程中,我们将研究创建一个立方体实体,并继承 PathAwareEntity . 这个实体将拥有模型和纹理。移动和物理机制将会在以后的教程中介绍。

创建并注册一个实体

创建一个继承自 PathAwareEntity 的类,这个类是我们创建的实体的大脑和主体

  1. /*
  2.   * 我们创建的实体继承自 PathAwareEntity, 它继承自 MobEntity, 而 MobEntity 继承自 LivingEntity.
  3.   *
  4.   * LivingEntity 拥有生命值,并且可以造成伤害。
  5.   * MobEntity 具有AI逻辑和移动控制。
  6.   * PathAwareEntity 提供额外的寻路系统,很多AI任务都需要用到寻路。
  7.   */
  8. public class CubeEntity extends PathAwareEntity {
  9.  
  10. public CubeEntity(EntityType<? extends PathAwareEntity> entityType, World world) {
  11. super(entityType, world);
  12. }
  13. }

你可以在 ENTITY_TYPE 注册类别下注册这个实体。Fabric 提供一个 FabricEntityTypeBuilder 类,继承自原版 EntityType.Builder 类。Fabric 提供的这个类提供了配置你的实体的追踪数值的额外方法。

  1. public class EntityTesting implements ModInitializer {
  2.  
  3. /*
  4.   * 使用“entitytesting:cube”作为ID注册我们的实体
  5.   *
  6.   * 这个实体注册在了 SpawnGroup#CREATURE 类别下,大多数的动物和友好或中立的生物都注册在这个类别下。
  7.   * 它有一个 0.75 × 0.75(或12个像素宽,即一个方块的3/4)大小的碰撞体积。
  8.   */
  9. public static final EntityType<CubeEntity> CUBE = Registry.register(
  10. Registries.ENTITY_TYPE,
  11. new Identifier("entitytesting", "cube"),
  12. FabricEntityTypeBuilder.create(SpawnGroup.CREATURE, CubeEntity::new).dimensions(EntityDimensions.fixed(0.75f, 0.75f)).build()
  13. );
  14.  
  15. @Override
  16. public void onInitialize() {
  17.  
  18. }
  19. }

实体需要属性(Attributes)渲染器(Renderer)才能起作用。

注册实体的属性

属性定义了一个生物拥有的基本数值:它有多少生命值?它能造成多少伤害?它有默认的装备栏吗?

大部分的原版实体都有一个静态方法用于返回它们的属性(比如说 ZombieEntity#createZombieAttributes )。 我们的自制实体没有任何独立的数值,目前,我们可以直接使用 MobEntity#createMobAttributes.

原版里有一个 DefaultAttributeRegistry 类用于注册这些属性。 想要使用它并不容易,所以Fabric提供一个 FabricDefaultAttributeRegistry 类。 你应该在你的Mod的初始化部分的某处注册默认的属性:

  1. public class EntityTesting implements ModInitializer {
  2.  
  3. public static final EntityType<CubeEntity> CUBE = [...];
  4.  
  5. @Override
  6. public void onInitialize() {
  7. /*
  8.   * 注册我们方块实体的默认属性。
  9.   * 属性是一个生物当前状态的数值,其中有攻击伤害和生命值等。
  10.   * 如果实体没有及时注册适当的属性,则游戏将崩溃。
  11.   *
  12.   * 在1.15中,它是通过重写实体类内部的方法来完成的。
  13.   * 大部分的原版实体都有一个静态方法(例如,ZombieEntity#createZombieAttributes)用于初始化它们的属性。
  14.   */
  15. FabricDefaultAttributeRegistry.register(CUBE, CubeEntity.createMobAttributes());
  16. }
  17. }

注册实体的渲染器

最后一个需要注册的是实体的渲染器。渲染器一般通过提供模型来决定实体的 外观MobEntityRenderer 是生物实体最好的选择。继承这个类需要重写一个用于提供纹理的方法,和三个用于父类构造的参数:

  • EntityRenderDispatcher 实例
  • Model 实体的模型
  • 实体阴影的大小,float类型

下面的代码展示了一个简单的实体渲染器,阴影大小是 0.5f,纹理的路径为 resources/assets/entitytesting/textures/entity/cube/cube.png。 注意:用到的纹理和模型将在下一步创建。

  1. /*
  2.   * 一个用来提供模型、阴影大小和纹理的渲染器
  3.   */
  4. public class CubeEntityRenderer extends MobEntityRenderer<CubeEntity, CubeEntityModel> {
  5.  
  6. public CubeEntityRenderer(EntityRenderDispatcher entityRenderDispatcher) {
  7. super(entityRenderDispatcher, new CubeEntityModel(), 0.5f);
  8. }
  9.  
  10. @Override
  11. public Identifier getTexture(CubeEntity entity) {
  12. return new Identifier("entitytesting", "textures/entity/cube/cube.png");
  13. }
  14. }

客户端初始化类中使用 EntityRendererRegistry 来注册这个渲染器:

  1. @Environment(EnvType.CLIENT)
  2. public class EntityTestingClient implements ClientModInitializer {
  3.  
  4. @Override
  5. public void onInitializeClient() {
  6. /*
  7.   * 方块实体渲染器的注册,提供模型、阴影大小和纹理的渲染器。
  8.   *
  9.   * 实体渲染器也可以在实体基于上下文进行渲染前(EndermanEntityRenderer#render). 操作模型。
  10.   */
  11. EntityRendererRegistry.INSTANCE.register(EntityTesting.CUBE, (dispatcher, context) -> {
  12. return new CubeEntityRenderer(dispatcher);
  13. });
  14. }
  15. }

创建模型和纹理

完成实体创建的最后一步是创建模型和纹理。模型定义了实体的结构 ,而纹理提供了实体的颜色。

标准的模型在类的顶部提供并在构造方法中初始化“部位(parts)”,即 ModelPart 对象,在构造方法中实例化,在 getTexturedModelData 方法中获得数据,然后在 render 方法中渲染它们。注意 setAnglesrenderEntityModel 类的抽象方法,必须重写。

  1. public class CubeEntityModel extends EntityModel<CubeEntity> {
  2.  
  3. private final ModelPart base;
  4.  
  5. public CubeEntityModel(ModelPart modelPart) {
  6. base = modelPart.getChild(EntityModelPartNames.CUBE);
  7. }
  8.  
  9. [...]
  10. }

在创建一个部位之后,我们需要添加一个形状(shape)。为此,我们必须为根部添加一个子形状。新部分的纹理位置在 .uv 中,其偏移位于 .cuboid 的前三个数字中,尺寸则为 .cuboid 的后三个数字。注意,模型的原点从拐角处开始,所以你需要一些偏移让它居中:

  1. public class CubeEntityModel extends EntityModel<CubeEntity> {
  2.  
  3. private final ModelPart base;
  4.  
  5. public CubeEntityModel() {
  6. [...]
  7. }
  8.  
  9. // 你可以使用 BlockBench,制作你的模型并为你的实体模型导出以得到这个方法。
  10. public static TexturedModelData getTexturedModelData() {
  11. ModelData modelData = new ModelData();
  12. ModelPartData modelPartData = modelData.getRoot();
  13. modelPartData.addChild(EntityModelPartNames.CUBE, ModelPartBuilder.create().uv(0, 0).cuboid(-6F, 12F, -6F, 12F, 12F, 12F), ModelTransform.pivot(0F, 0F, 0F));
  14. }
  15. }

我们的实体模型现在有了一个 12x12x12 的方块(大约一个方块的75%),并以0, 0, 0为中心。setAngles 用于模型的动画,但是目前我们留空。render 用来告诉游戏我们的实体出现在哪。注意标准的原版模型通常看起来比它们的碰撞体积更大,因此,我们在这里把模型变小。

  1. public class CubeEntityModel extends EntityModel<CubeEntity> {
  2.  
  3. private final ModelPart base;
  4.  
  5. public CubeEntityModel() [...]
  6.  
  7. @Override
  8. public void setAngles(CubeEntity entity, float limbAngle, float limbDistance, float animationProgress, float headYaw, float headPitch) {
  9.  
  10. }
  11.  
  12. @Override
  13. public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) {
  14. ImmutableList.of(this.base).forEach((modelRenderer) -> {
  15. modelRenderer.render(matrices, vertices, light, overlay, red, green, blue, alpha);
  16. });
  17. }
  18. }

要完成我们的模型,我们还需要一个纹理文件。默认的纹理大小是64宽、32高的;你可以通过在 texturedModelData 添加返回值来改变。我们使用 64×64 的纹理:

  1. public class CubeEntityModel extends EntityModel<CubeEntity> {
  2.  
  3. private final ModelPart base;
  4.  
  5. public static TexturedModelData getTexturedModelData() {
  6. [...]
  7. return TexturedModelData.of(modelData, 64, 64);
  8. }
  9.  
  10. [...]
  11. }

生成你的实体

记得将客户端入口点添加到 fabric.mod.json 中,像这样:

  "entrypoints": {
    "main": [
      "mod.fabricmc.entitytesting.EntityTesting"
    ],
    "client": [
      "mod.fabricmc.entitytesting.EntityTestingClient"
    ]
  },

你可以在游戏内使用 /summon entitytesting:cube 来生成你的实体,按下 F3+b 可以查看它的碰撞体积:

注意:如果你的实体没有继承 LivingEntity,你需要创建你自己的封包处理器。你可以通过网络 API 来完成,也可以对 ClientPlayNetworkHandler#onEntitySpawn 使用 mixin。

zh_cn/tutorial/entity.txt · Last modified: 2023/01/15 01:23 by solidblock