This is an old revision of the document!
创建一个实体
本文的源码托管于 这个仓库 的 entity 分支.
实体(Entity)是游戏世界里的一种可以根据附加的逻辑移动的物体,包括:
- 矿车
- 箭
- 船
生物实体(Living Entity)是拥有生命值,并且可以造成伤害的实体。 为了实现不同的功能,生物实体有着不同的分支类型,其中有:
HostileEntity
敌对实体,用于僵尸、苦力怕、骷髅等AnimalEntity
动物实体,用于羊、牛、猪等WaterCreatureEntity
水生物实体,用于可以游泳的实体FishEntity
鱼实体,用于鱼
具体继承、使用哪个取决于你的需求和目的。
随着开发进度的推进,对于特定的任务,实体的逻辑变得越来越具体、简明。有以下两种继承自 LivingEntity
的实体:
MobEntity
PathAwareEntity
MobEntity
具有AI逻辑和移动控制。 PathAwareEntity
提供额外的寻路系统,很多AI任务都需要用到寻路。
在这篇教程中,我们将研究创建一个立方体实体,并继承 PathAwareEntity
. 这个实体将拥有模型和纹理。移动和物理机制将会在以后的教程中介绍。
创建并注册一个实体
创建一个继承自 PathAwareEntity
的类,这个类是我们创建的实体的大脑和主体
/* * 我们创建的实体继承自 PathAwareEntity, 它继承自 MobEntity, 而 MobEntity 继承自 LivingEntity. * * LivingEntity 拥有生命值,并且可以造成伤害。 * MobEntity 具有AI逻辑和移动控制。 * PathAwareEntity 提供额外的寻路系统,很多AI任务都需要用到寻路。 */ public class CubeEntity extends PathAwareEntity { public CubeEntity(EntityType<? extends PathAwareEntity> entityType, World world) { super(entityType, world); } }
你可以在 ENTITY_TYPE
注册类别下注册这个实体。Fabric 提供一个 FabricEntityTypeBuilder
类,继承自原版 EntityType.Builder
类。Fabric 提供的这个类提供了配置你的实体的追踪数值的额外方法。
public class EntityTesting implements ModInitializer { /* * 使用“entitytesting:cube”作为ID注册我们的实体 * * 这个实体注册在了 SpawnGroup#CREATURE 类别下,大多数的动物和友好或中立的生物都注册在这个类别下。 * 它有一个 0.75 × 0.75(或12个像素宽,即一个方块的3/4)大小的碰撞体积。 */ Registries.ENTITY_TYPE, new Identifier("entitytesting", "cube"), FabricEntityTypeBuilder.create(SpawnGroup.CREATURE, CubeEntity::new).dimensions(EntityDimensions.fixed(0.75f, 0.75f)).build() ); @Override public void onInitialize() { } }
实体需要属性(Attributes)和渲染器(Renderer)才能起作用。
注册实体的属性
属性定义了一个生物拥有的基本数值:它有多少生命值?它能造成多少伤害?它有默认的装备栏吗?
大部分的原版实体都有一个静态方法用于返回它们的属性(比如说 ZombieEntity#createZombieAttributes
)。
我们的自制实体没有任何独立的数值,目前,我们可以直接使用 MobEntity#createMobAttributes
.
原版里有一个 DefaultAttributeRegistry
类用于注册这些属性。
想要使用它并不容易,所以Fabric提供一个 FabricDefaultAttributeRegistry
类。
你应该在你的Mod的初始化部分的某处注册默认的属性:
public class EntityTesting implements ModInitializer { public static final EntityType<CubeEntity> CUBE = [...]; @Override public void onInitialize() { /* * 注册我们方块实体的默认属性。 * 属性是一个生物当前状态的数值,其中有攻击伤害和生命值等。 * 如果实体没有及时注册适当的属性,则游戏将崩溃。 * * 在1.15中,它是通过重写实体类内部的方法来完成的。 * 大部分的原版实体都有一个静态方法(例如,ZombieEntity#createZombieAttributes)用于初始化它们的属性。 */ FabricDefaultAttributeRegistry.register(CUBE, CubeEntity.createMobAttributes()); } }
注册实体的渲染器
最后一个需要注册的是实体的渲染器。渲染器一般通过提供模型来决定实体的 *外观*。
MobEntityRenderer
是生物实体最好的选择。继承这个类需要重写一个用于提供贴图的方法,和三个用于父类构造的参数:
EntityRenderDispatcher
一个EntityRenderDispatcher对象Model
实体的模型- 实体阴影的大小,
float
类型
下面的代码展示了一个简单的实体渲染器,它的阴影大小是0.5f, 贴图的路径为 resources/assets/entitytesting/textures/entity/cube/cube.png
.
注意:用到的贴图和模型将在下一步创建。
/* * 一个用来提供模型、阴影大小和贴图的渲染器 */ public class CubeEntityRenderer extends MobEntityRenderer<CubeEntity, CubeEntityModel> { public CubeEntityRenderer(EntityRenderDispatcher entityRenderDispatcher) { super(entityRenderDispatcher, new CubeEntityModel(), 0.5f); } @Override public Identifier getTexture(CubeEntity entity) { return new Identifier("entitytesting", "textures/entity/cube/cube.png"); } }
在客户端初始化类中使用 EntityRendererRegistry
来注册这个渲染器:
public class EntityTestingClient implements ClientModInitializer { @Override public void onInitializeClient() { /* * 方块实体渲染器的注册,提供模型、阴影大小和贴图的渲染器。 * * 实体渲染器也可以在实体基于上下文进行渲染前(EndermanEntityRenderer#render). 操作模型。 */ EntityRendererRegistry.INSTANCE.register(EntityTesting.CUBE, (dispatcher, context) -> { return new CubeEntityRenderer(dispatcher); }); } }
创建模型和贴图
完成实体创建的最后一步是创建模型和贴图。 模型定义了实体的 结构 ,而贴图提供了实体的颜色。
标准的模型在类的顶部提供并在构造方法中初始化“部位(parts)”, 或 ModelPart
对象,
然后在 render
方法中渲染它们。
注意 setAngles
和 render
是 EntityModel
类的抽象方法,必须重写。
ModelPart
有几个不同的构造方法,其中最简单的一个需要以下参数:
- 当前模型对象
- textureOffsetU,
int
类型的贴图偏移值 - textureOffsetV,
int
类型的贴图偏移值
贴图偏移值指在你的贴图文件中,当前模型贴图的位置。
我们的实体的一个简单的方块,所以 ModelPart
的贴图的偏移为0, 0.
public class CubeEntityModel extends EntityModel<CubeEntity> { private final ModelPart base; public CubeEntityModel() { base = new ModelPart(this, 0, 0); } [...] }
在创建一个部位之后,我们需要为他添加一个形状(shape)。 注意,模型的原点从拐角处开始,所以你需要一些偏移让它居中:
public class CubeEntityModel extends EntityModel<CubeEntity> { private final ModelPart base; public CubeEntityModel() { base = new ModelPart(this, 0, 0); base.addCuboid(-6, -6, -6, 12, 12, 12); } [...] }
O我们的实体模型现在有了一个12x12x12的方块(大约一个方块的75%),并以0, 0, 0为中心。
setAngles
用于模型的动画,但是目前我们留空。
render
用来告诉游戏我们的实体出现在哪。注意标准的原版模型通常看起来比它们的碰撞体积更大,因此,我们在这里把模型变小。
public class CubeEntityModel extends EntityModel<CubeEntity> { private final ModelPart base; public CubeEntityModel() [...] @Override public void setAngles(CubeEntity entity, float limbAngle, float limbDistance, float animationProgress, float headYaw, float headPitch) { } @Override public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) { // 把模型变小 matrices.translate(0, 1.125, 0); // 渲染方块实体 base.render(matrices, vertices, light, overlay); } }
我们还需要添加一个贴图文件来完成它。默认的贴图大小是64宽、32高的;
你可以通过改变模型构造函数里的 textureWidth
和 textureHeight
字段来改变它。
我们使用64×64的贴图:
public class CubeEntityModel extends EntityModel<CubeEntity> { private final ModelPart base; public CubeEntityModel() { this.textureHeight = 64; this.textureWidth = 64; [...] } [...] }