3.1.1 新的生物和对应的刷怪蛋
1. 新的实体生物
就像刚刚我们看到的一样,如果注册一个新的实体生物的话,我们就需要首先建立一个EntityLiving
的子类。读者可以根据自己的需要和代码的利用,选择适合的类进行继承,并添加上适当的接口。
新建包com.github.ustc_zzzz.fmltutor.entity
,并在其下新建一个文件EntityGoldenChicken.java
:
src/main/java/com/github/ustc_zzzz/fmltutor/entity/EntityGoldenChicken.java:
package com.github.ustc_zzzz.fmltutor.entity;
import net.minecraft.entity.passive.EntityChicken;
import net.minecraft.world.World;
public class EntityGoldenChicken extends EntityChicken
{
public EntityGoldenChicken(World worldIn)
{
super(worldIn);
}
@Override
public void onLivingUpdate()
{
super.onLivingUpdate();
}
}
这里我们想要做的是一个金色的鸡,所以我们这里继承了EntityChicken
类。
首先,一个实体需要有一个传入世界参数的构造函数,如果希望这个实体生物可以在世界自然生成,这一个传入世界参数的构造函数是必要的。这里简单继承父类的就可以了。
然后,我们就来到了一个相对实体生物来说比较重要的一个方法,也就是名为onLivingUpdate
的方法。这个方法用于对实体生物的更新,包括更新实体的AI等。若干gametick更新一次,也就是这个方法被调用一次,在本部分的稍后作者会讲到如何设置这个更新频率。这里我们先显式覆写这个方法,将来我们也会向其中添加我们想要的内容。
在onLivingUpdate
方法中额外添加的内容,大多是一些不属于人工智能的行为,比如骷髅在阳光下着火,动物会相互发送爱心(不包括繁殖)等。那些AI的添加,我们会在后面的部分中进行讲解。
2. 掉落物
我们添加下面的代码:
src/main/java/com/github/ustc_zzzz/fmltutor/entity/EntityGoldenChicken.java(部分):
@Override
protected void dropFewItems(boolean arg1, int arg2)
{
if (this.rand.nextInt(10) == 0)
{
this.dropItem(ItemLoader.goldenEgg, 1);
}
super.dropFewItems(arg1, arg2);
}
在Minecraft中,通过调用实体生物的dropFewItems
方法来使得实体生物掉落物品,一般会在实体生物死亡时调用。该方法的第一个参数用于给出该实体生物是被玩家攻击致死,还是由于自然原因等而死,比如蜘蛛眼的获取,就必须保证对应的蜘蛛是玩家攻击致死的。该方法的第二个参数,指的是抢夺等级,也就是玩家攻击生物致死时的使用的武器的抢夺等级。这里我们覆写了这个方法,以10%的概率添加一种新的掉落。
如果没有多于一个的掉落,或者数量异常的掉落,可以覆写对应类的getDropItem
方法,通过覆写这个方法,我们就可以使用一种比较平凡的方式掉落我们想要的物品。
3. 注册这个实体生物
在包com.github.ustc_zzzz.fmltutor.entity
下新建一个文件EntityLoader.java
:
src/main/java/com/github/ustc_zzzz/fmltutor/entity/EntityLoader.java:
package com.github.ustc_zzzz.fmltutor.entity;
import com.github.ustc_zzzz.fmltutor.FMLTutor;
import net.minecraft.entity.Entity;
import net.minecraftforge.fml.common.registry.EntityRegistry;
public class EntityLoader
{
private static int nextID = 0;
public EntityLoader()
{
registerEntity(EntityGoldenChicken.class, "GoldenChicken", 80, 3, true);
}
private static void registerEntity(Class<? extends Entity> entityClass, String name, int trackingRange,
int updateFrequency, boolean sendsVelocityUpdates)
{
EntityRegistry.registerModEntity(entityClass, name, nextID++, FMLTutor.instance, trackingRange, updateFrequency,
sendsVelocityUpdates);
}
}
正如读者所见的那样,我们通过Forge提供的EntityRegistry
类注册一个实体,我们先看看对应的注册实体的方法,也就是registerModEntity
方法:
public static void registerModEntity(Class<? extends Entity> entityClass, String entityName, int id, Object mod, int trackingRange, int updateFrequency, boolean sendsVelocityUpdates) {...}
- 第一个参数需要传入这个实体对应的class实例,这里传入
EntityGoldenChicken.class
是没有什么问题的 - 第二个参数需要传入这个实体的名称,不过这里我们和物品、方块等不一样,实体的名称建议使用大写驼峰式,这里传入的就是
GoldenChicken
- 第三个参数表示这个实体类型的ID,同一个Mod的每一个实体类型的ID要不同,这里是通过递增处理的
- 第四个参数表示这个实体对应的Mod实例,这里我们使用主类中提供的Mod实例,也没有什么问题
- 第五个参数表示这个实体的跟踪半径,也就是说如果这个实体距离玩家一量超过对应的大小,这个实体就不更新了,一般情况下,生物设置成64格是比较合理的,当然有的实体有特殊需要,这个参数设置得比较大
- 第六个参数表示这个实体的更新频率,对于生物来说,每3 gametick一更新是比较常见的做法,当然有的会设置成特别长,有10 gametick,20 gametick,甚至整数的最大值,也就是不更新
- 最后一个参数表示是否同步实体的速度更新,对于一些实体,比如静止的实体,一些会手动更新数据的BOSS,是没有必要的,但是对于实体生物来说是有必要的,这里设置为真
然后在CommonProxy
的preInit
阶段中注册:
src/main/java/com/github/ustc_zzzz/fmltutor/common/CommonProxy.java(部分):
public void preInit(FMLPreInitializationEvent event)
{
new ConfigLoader(event);
new CreativeTabsLoader(event);
new FluidLoader(event);
new ItemLoader(event);
new BlockLoader(event);
new OreDictionaryLoader(event);
new PotionLoader(event);
new EntityLoader();
}
到目前为止,一个实体已经注册完成了。不过还有一点小的事情需要处理,那就是对应的语言文件:
src/main/resources/assets/fmltutor/lang/en_US.lang(部分):
entity.fmltutor.GoldenChicken.name=Golden Chicken
src/main/resources/assets/fmltutor/lang/zh_CN.lang(部分):
entity.fmltutor.GoldenChicken.name=黄金鸡
语言文件中出现的那个GoldenChicken
,代表的就是实体的名字。
4. 注册刷怪蛋
我们新添加下面的代码:
src/main/java/com/github/ustc_zzzz/fmltutor/entity/EntityLoader.java(部分):
public EntityLoader()
{
registerEntity(EntityGoldenChicken.class, "GoldenChicken", 80, 3, true);
registerEntityEgg(EntityGoldenChicken.class, 0xffff66, 0x660000);
}
private static void registerEntityEgg(Class<? extends Entity> entityClass, int eggPrimary, int eggSecondary)
{
EntityRegistry.registerEgg(entityClass, eggPrimary, eggSecondary);
}
注册刷怪蛋的方法其实很简单,调用EntityRegistry
类的registerEgg
方法就可以了。这个方法的第一个参数表示实体对应的class实例,后面的两个参数表示刷怪蛋的主色和副色,也就是蛋本身的颜色和对应的斑点,注册完成后打开游戏,我们就可以看到下面的游戏画面了:
拿着刷怪蛋右键方块,我们就可以产生一个新的实体了。不过这里我们没有讲解如何注册对应的实体的渲染,所以现在的实体使用的渲染还是它父类的渲染,也就是鸡的样子,在后面的部分我们会讲解如何自定义渲染。
5. 使用代码生成实体
其实在前面的部分我们已经对在世界上生成实体,有了一定的运用了,我们在这里再简单提一下,为了演示,我们为这个Mod加一点小小的特性:
src/main/java/com/github/ustc_zzzz/fmltutor/common/EventLoader.java(部分):
@SubscribeEvent
public void onPlayerClickGrassBlock(PlayerRightClickGrassBlockEvent event)
{
if (!event.world.isRemote)
{
ItemStack heldItem = event.entityPlayer.getHeldItem();
if (ItemLoader.goldenEgg.equals(heldItem.getItem()))
{
EntityLiving entityLiving = new EntityGoldenChicken(event.world);
BlockPos pos = event.pos;
entityLiving.setPositionAndUpdate(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
--heldItem.stackSize;
event.world.spawnEntityInWorld(entityLiving);
return;
}
BlockPos pos = event.pos;
Entity tnt = new EntityTNTPrimed(event.world, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, null);
event.world.spawnEntityInWorld(tnt);
event.entityPlayer.triggerAchievement(AchievementLoader.explosionFromGrassBlock);
}
}
当玩家达到对应的触发条件(拿着金蛋右击草块)的时候:
src/main/java/com/github/ustc_zzzz/fmltutor/common/EventLoader.java(部分):
EntityLiving entityLiving = new EntityGoldenChicken(event.world);
BlockPos pos = event.pos;
entityLiving.setPositionAndUpdate(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
我们先实例化一个新的实体对应的类,然后设置一下这个实体的位置。
src/main/java/com/github/ustc_zzzz/fmltutor/common/EventLoader.java(部分):
event.world.spawnEntityInWorld(entityLiving);
最后我们通过spawnEntityInWorld
方法,生成了这个实体。