在阅读这一小节之前,我要分为1.11以下与1.11以上(包括1.11)两个版本进行讲解,因为在1.11这个版本出现时,Mojang新增了一个叫MinecraftKey的东西,取代了1.11之前使用Integer来表示实体网络ID,当然了它在1.12里面也用于advancement的一个使用
由于在1.8-1.13对实体的实现都是差不多的,所以对于1.11这个分水岭我只讲它们两个注册的不同之处,具体实现请读者自行反编译阅读代码实现!
在BukkitAPI当中,没有什么特殊的操作可以用于自定义一个实体(如果有可以告知我),所以我们就需要进入NMS下对实体进行一个操作
我们新建一个类叫做 MyZombie,之后让它继承于EntityZombie
- import net.minecraft.server.v1_10_R1.EntityZombie;
- public class MyZombie extends EntityZombie {
-
- }
复制代码 然后我们需要实现一个构造方法,这里我使用BukkitAPI里的Location作为它的参数
public MyZombie(Location location) {
super(((CraftWorld) location.getWorld()).getHandle());
this.setCustomName("我的僵尸");
this.setCustomNameVisible(true);
// 需要设置该实体的Position才会出现在对应的Location
this.setPosition(location.getX(), location.getY(), location.getZ());
}
之后我们可以复写一些在EntityZombie或其父类里的一些方法,比如我这里复写这个方法 (这里要注意的是不同版本的NMS该方法名会有些不同,所以可以通过该方法的参数名来判断是哪个参数) @Override
public boolean a(EntityHuman entityhuman, EnumHand enumhand, @Nullable ItemStack itemstack) {
// 以下的if判断了这些东西
// 空值判断
// 物品类型判断是否为金苹果
// 判断附加值(耐久, 损坏值)
// 判断是否为村民
// 判断是否拥有虚弱Buff
if ((itemstack != null) && (itemstack.getItem() == Items.GOLDEN_APPLE) && (itemstack.getData() == 0) && (isVillager()) && (hasEffect(MobEffects.WEAKNESS))) {
if (!entityhuman.abilities.canInstantlyBuild) { // 判断该玩家是否是 可以立即建立的模式 即创造模式
itemstack.count -= 1; // 减少物品
}
if (!this.world.isClientSide) { // 判断是否为客户端
a(this.random.nextInt(2401) + 3600); // 执行下方截图的代码
}
return true;
}
return false;
}
在这里,如果你有丰富的游戏经验的话,不难看出上方的方法其实就是将僵尸村民吃金苹果转换为普通村民的一个方法,那么在MCP中它又叫什么呢?这里我使用了MCP Query这个工具查到了以下的内容
srg: func_184645_a
name: processInteract
side: Both
notch: zi.a
return: boolean
根据上方的信息我们不难看出,在MCP当中,这个NMS下的a()方法其实叫processInteract,并且是一个双向的方法,可以用于客户端上也可以用与服务器上
那么我们重写一下这个方法
- public boolean a(EntityHuman entityhuman, EnumHand enumhand, @Nullable ItemStack itemstack) {
- if (itemstack == null) {
- return false;
- }
- if (itemstack.getItem() == Items.APPLE) {
- if (!entityhuman.abilities.canInstantlyBuild) {
- itemstack.count -= 1;
- }
- entityhuman.getBukkitEntity().sendMessage("真香");
- } else {
- entityhuman.getBukkitEntity().sendMessage("我不喜欢这个...");
- }
- return false;
- }
复制代码
如果你自定义的实体是有生命的实体那么就需要对其注册!
Below 1.11 Version —— 低于1.11版本的注册方式
在NMS当中,有个类叫做EntityTypes,该类主要用于注册实体,(具体的注册方式请读者自己去反编译查看),我们可以通过反射的方式来进行注册,看我下面的操作
static {
((Map) getPrivateField("c", EntityTypes.class, null)).put("MyZombie", MyZombie.class);
((Map) getPrivateField("d", EntityTypes.class, null)).put(MyZombie.class, "MyZombie");
// 如果我们想覆盖NMS内的僵尸的话那么就可以加上下面那行注释起来的代码
//((Map) getPrivateField("e", EntityTypes.class, null)).put(54, MyZombie.class);
((Map) getPrivateField("f", EntityTypes.class, null)).put(MyZombie.class, 54);
(Map) getPrivateField("g", EntityTypes.class, null)).put("MyZombie", 54);
}
private static Object getPrivateField(String fieldName, Class clazz, Object object) {
Field field;
Object obj = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
obj = field.get(object);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
return obj;
}
(论坛日常吞代码)
我们在MyZombie类下新增了一个静态初始化块,用于注册我们的实体,而下面我们需要利用反射获取到EntityTypes下对于所有实体控制的那几个Map(别看它多,其实就是两个双向的Map就可以达到的目的)
这里有人会问了,那个54是什么意思呢?
在1.11版本之前,所有的实体都会有一个实体ID,也就是我们上方代码中字段e的那个Map所表示的那样,实体ID -> 对应实体的class
不同的实体会有不同的实体ID,所有的实体ID都可以在这里找到~
https://wiki.vg/Entity_metadata#Mobs (那个表格,最左边就是,最右边的是1.11版本当中的minecraftkey)
这样我们就做好了在1.11版本以下的实体的一个注册
After 1.11 Version —— 高于1.11版本的注册方式
在1.11之后,注册就变得简单起来了,因为在EntityTypes当中,之前的那些private Map都改成了public static RegisterMaterial,方便大众(当然我不懂是不是md_5自己改的..)
这里要注意的是在不同版本该变量名有所不同例如1.13中它变成了EntityTypes.REGISTRY - public class MyZombie extends EntityZombie {
- private static MinecraftKey minecraftKey;
- static {
- // 给我们的自定义实体做一个MinecraftKey
- minecraftKey = new MinecraftKey("my_zombie"); // minecraft:my_zombie
- // 实体注册
- EntityTypes.d.add(minecraftKey); // 将此key添加至EntityTypes的列表里
- EntityTypes.b.a(54, minecraftKey, MyZombie.class); // 对其注册
- // 如果想取消对该实体的注册那么可以执行下方的代码
- EntityTypes.d.remove(minecraftKey);
- MinecraftKey oldKey = EntityTypes.b.b(EntityZombie.class); // minecraft:zombie
- EntityTypes.b.a(54, oldKey, EntityZombie.class); // 把54号id所对应的minecraft:key和entity class进行覆盖
- }
- public static MinecraftKey getMinecraftKey() {
- return minecraftKey;
- }
- }
复制代码从上方的代码可以看出,我们不再需要反射来对实体进行注册了
这样你就学会了怎么在1.11版本之后对实体进行注册了
通过反编译查找到World#spawnEntity可以看出在CraftWorld下会有一个方法叫做addEntity(net.minecraft.server.XXX.Entity entity, SpawnReason reason) 于是我们可以得到下方的代码 Location location = player.getLocation(); // 对自定义进行实例化 MyZombie myZombie = new MyZombie(location); // 生成至世界内 CraftWorld craftWorld = (CraftWorld) location.getWorld(); craftWorld.addEntity(myZombie, CreatureSpawnEvent.SpawnReason.CUSTOM);
具体效果:
|