零、什么是实体
实体(Entity)包括在Minecraft中所有动态的、移动中的对象。例如游戏中的怪物僵尸骷髅等,船和矿车,受重力影响的方块如下落的沙子铁砧等。
我们今天要加入的东西就是一个雪球怪,它拥有和岩浆怪一样的分裂能力,不同的是,他不能免疫灼烧伤害,和雪傀儡会受到比较热的自然环境的伤害,并且走到哪里哪里有雪。
一、实体继承树
这张图有点糊,但是没办法,我从网上找不到其他图了。
从这张继承树中我们可以岩浆怪继承自史莱姆;雪傀儡继承自抽象类傀儡实体(傀儡实体有三个子类,雪傀儡,铁傀儡和凋零。都是非抽象类);再进一步翻阅forge源码,我们还会发现雪傀儡还实现了两个接口:IRangedAttackMo用于实现怪物远程攻击,女巫,骷髅等也实现了这个接口,IShearable用于实现可剪裁,羊雪傀儡都实现了。
岩浆怪:
public class EntityMagmaCube extends EntitySlime{
/* codes */
}
雪傀儡:
public class EntitySnowman extends EntityGolem implements IRangedAttackMob, net.minecraftforge.common.IShearable{
/* codes */
}
二、编写雪球怪的代码:
1.首先既然雪球怪要和岩浆怪类似,那肯定也要继承史莱姆
public class EntitySnowCube extends EntitySlime {
/* codes */
}
2.然后我们从岩浆怪的源码里面拷一段给雪球怪
public class EntitySnowCube extends EntitySlime {
public EntitySnowCube(World worldIn) {
super(worldIn);
}
public void registerFixesSnowCube(DataFixer fixer) {
EntityLiving.registerFixesMob(fixer, EntitySnowCube.class);
}
/**
* 改变了了雪球怪的速度,让他和岩浆怪一样(这个方法是粘贴自岩浆怪的)
*/
@Override
protected void applyEntityAttributes() {
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.20000000298023224D);
}
/**
* @return 设置是否能召唤,只要游戏难度不是和平模式就能召唤
*/
@Override
public boolean getCanSpawnHere() {
return this.world.getDifficulty() != EnumDifficulty.PEACEFUL;
}
/**
* 这个方法依旧是粘贴的岩浆怪的,他的作用应该是用来设置史莱姆的大小的(大史莱姆,中史莱姆,小史莱姆)
* 这个方法中显示调用了超类史莱姆的方法设置好了大小,然后再把生物的护甲值升高,所以岩浆怪才会比普通史莱姆难打。
* @param size
* @param resetHealth
*/
@Override
protected void setSlimeSize(int size, boolean resetHealth)
{
super.setSlimeSize(size, resetHealth);
this.getEntityAttribute(SharedMonsterAttributes.ARMOR).setBaseValue((double)(size * 3));
}
/**
* 跳跃延迟,直接粘贴岩浆怪的数据了
*/
@Override
protected int getJumpDelay()
{
return super.getJumpDelay() * 4;
}
/**
* @return 能否伤害玩家
* 这里和岩浆怪保持一致,无论大小都能伤害,但其实史莱姆这里是return !this.isSmallSlime();小的没有攻击力
*/
@Override
protected boolean canDamagePlayer()
{
return true;
}
/**
* @return 攻击强度,这里和岩浆怪保持一致,是史莱姆攻击强度加二
*/
@Override
protected int getAttackStrength()
{
return super.getAttackStrength() + 2;
}
}
这样雪球怪就拥有了一部分和岩浆怪一样的属性
需要注意的是,岩浆怪的构造方法其实比史莱姆和雪球怪的多了一行。
public EntityMagmaCube(World worldIn)
{
super(worldIn);
this.isImmuneToFire = true;
}
这多的一行是的岩浆怪可以免疫火焰,那我们的雪球怪自然要把这一行删掉。
3.设置一些不一样的属性:
/**
* 该方法继承自史莱姆
* @return 设置的粒子效果
*/
@Override
protected EnumParticleTypes getParticleType() {
return EnumParticleTypes.SNOWBALL;
}
/**
* @return 这个返回值是雪球怪死后会分裂成什么
*/
@Override
protected EntitySlime createInstance()
{
return new EntitySnowCube(this.world);
}
/**
* 这个方法是获得掉落物表,继承自EntityLiving
* @return 返回掉落物表
*/
@Override
@Nullable
protected ResourceLocation getLootTable()
{
// 如果是小史莱姆才会掉落雪球
return this.isSmallSlime() ? LootTableList.ENTITIES_SNOWMAN : LootTableList.EMPTY;
}
/**
* 被攻击后发出的声音
* @return 这里和雪傀儡一致
*/
@Override
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
{
return SoundEvents.ENTITY_SNOWMAN_HURT;
}
/**
* 死亡音效
* @return 和雪傀儡一致
*/
@Override
protected SoundEvent getDeathSound()
{
return SoundEvents.ENTITY_SNOWMAN_DEATH;
}
/**
* 被挤压的音效
* @return 雪傀儡没有这个音效,所以这里用的是雪傀儡的环境音效
*/
@Override
protected SoundEvent getSquishSound()
{
return SoundEvents.ENTITY_SNOWMAN_AMBIENT;
}
/**
* 跳跃音效
* @return 使用扔雪球的音效
*/
protected SoundEvent getJumpSound()
{
return SoundEvents.ENTITY_SNOWBALL_THROW;
}
/*
* 此外还有几个方法继承自史莱姆
* jump() handleJumpLava() handleJumpWater() 等,
* 只知道是关于跳跃,其他具体情况就不知道了
* 这里没有拷贝岩浆球的数据,让雪球怪跟史莱姆的这些数据保持一致好了
*/
5.让雪球怪具有一部分雪傀儡的属性:
首先我们翻阅雪傀儡的源码,找到其中令雪傀儡走路留雪,见水死等属性的方法:
public void onLivingUpdate()
{
super.onLivingUpdate();
if (!this.world.isRemote)
{
int i = MathHelper.floor(this.posX);
int j = MathHelper.floor(this.posY);
int k = MathHelper.floor(this.posZ);
if (this.isWet())
{
this.attackEntityFrom(DamageSource.DROWN, 1.0F);
}
if (this.world.getBiome(new BlockPos(i, 0, k)).getTemperature(new BlockPos(i, j, k)) > 1.0F)
{
this.attackEntityFrom(DamageSource.ON_FIRE, 1.0F);
}
if (!net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this))
{
return;
}
for (int l = 0; l < 4; ++l)
{
i = MathHelper.floor(this.posX + (double)((float)(l % 2 * 2 - 1) * 0.25F));
j = MathHelper.floor(this.posY);
k = MathHelper.floor(this.posZ + (double)((float)(l / 2 % 2 * 2 - 1) * 0.25F));
BlockPos blockpos = new BlockPos(i, j, k);
if (this.world.getBlockState(blockpos).getMaterial() == Material.AIR && this.world.getBiome(blockpos).getTemperature(blockpos) < 0.8F && Blocks.SNOW_LAYER.canPlaceBlockAt(this.world, blockpos))
{
this.world.setBlockState(blockpos, Blocks.SNOW_LAYER.getDefaultState());
}
}
}
}
显而易见是这个,这个方法继承自抽象类EntityLiving,而史莱姆也继承自这个抽象类,所以我们完全可以让雪球怪也继承这个方法。
6.完整的雪球怪代码:
package com.darkill.examplemod.entity;
import net.minecraft.block.material.Material;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.monster.EntitySlime;
import net.minecraft.init.Blocks;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.datafix.DataFixer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.World;
import net.minecraft.world.storage.loot.LootTableList;
import javax.annotation.Nullable;
public class EntitySnowCube extends EntitySlime {
public EntitySnowCube(World worldIn) {
super(worldIn);
}
public void registerFixesSnowCube(DataFixer fixer) {
EntityLiving.registerFixesMob(fixer, EntitySnowCube.class);
}
/**
* 改变了了雪球怪的速度,让他和岩浆怪一样(这个方法是粘贴自岩浆怪的)
*/
@Override
protected void applyEntityAttributes() {
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.20000000298023224D);
}
/**
* @return 设置是否能召唤,只要游戏难度不是和平模式就能召唤
*/
@Override
public boolean getCanSpawnHere() {
return this.world.getDifficulty() != EnumDifficulty.PEACEFUL;
}
/**
* 从雪傀儡的源码中拷贝了一个函数给雪球怪
* 使雪球怪拥有一些雪傀儡的属性。
*/
@Override
public void onLivingUpdate()
{
super.onLivingUpdate();
if (!this.world.isRemote)
{
int i = MathHelper.floor(this.posX);
int j = MathHelper.floor(this.posY);
int k = MathHelper.floor(this.posZ);
if (this.isWet())
{
this.attackEntityFrom(DamageSource.DROWN, 1.0F);
}
if (this.world.getBiome(new BlockPos(i, 0, k)).getTemperature(new BlockPos(i, j, k)) > 1.0F)
{
this.attackEntityFrom(DamageSource.ON_FIRE, 1.0F);
}
if (!net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this))
{
return;
}
for (int l = 0; l < 4; ++l)
{
i = MathHelper.floor(this.posX + (double)((float)(l % 2 * 2 - 1) * 0.25F));
j = MathHelper.floor(this.posY);
k = MathHelper.floor(this.posZ + (double)((float)(l / 2 % 2 * 2 - 1) * 0.25F));
BlockPos blockpos = new BlockPos(i, j, k);
if (this.world.getBlockState(blockpos).getMaterial() == Material.AIR && this.world.getBiome(blockpos).getTemperature(blockpos) < 0.8F && Blocks.SNOW_LAYER.canPlaceBlockAt(this.world, blockpos))
{
this.world.setBlockState(blockpos, Blocks.SNOW_LAYER.getDefaultState());
}
}
}
}
/**
* 这个方法依旧是粘贴的岩浆怪的,他的作用应该是用来设置史莱姆的大小的(大史莱姆,中史莱姆,小史莱姆)
* 这个方法中显示调用了超类史莱姆的方法设置好了大小,然后再把生物的护甲值升高,所以岩浆怪才会比普通史莱姆难打。
* @param size
* @param resetHealth
*/
@Override
protected void setSlimeSize(int size, boolean resetHealth)
{
super.setSlimeSize(size, resetHealth);
this.getEntityAttribute(SharedMonsterAttributes.ARMOR).setBaseValue((double)(size * 3));
}
/**
* 该方法继承自史莱姆
* @return 设置的粒子效果
*/
@Override
protected EnumParticleTypes getParticleType() {
return EnumParticleTypes.SNOWBALL;
}
/**
* @return 这个返回值是雪球怪死后会分裂成什么
*/
@Override
protected EntitySlime createInstance()
{
return new EntitySnowCube(this.world);
}
/**
* 这个方法是获得掉落物表,继承自EntityLiving
* @return 返回掉落物表
*/
@Override
@Nullable
protected ResourceLocation getLootTable()
{
// 如果是小史莱姆才会掉落雪球
return this.isSmallSlime() ? LootTableList.ENTITIES_SNOWMAN : LootTableList.EMPTY;
}
/**
* 跳跃延迟,直接粘贴岩浆怪的数据了
*/
@Override
protected int getJumpDelay()
{
return super.getJumpDelay() * 4;
}
/*
* 此外还有几个方法继承自史莱姆
* jump() handleJumpLava() handleJumpWater() 等,
* 只知道是关于跳跃,其他具体情况就不知道了
* 这里没有拷贝岩浆球的数据,让雪球怪跟史莱姆的这些数据保持一致好了
*/
/**
* @return 能否伤害玩家
* 这里和岩浆怪保持一致,无论大小都能伤害,但其实史莱姆这里是return !this.isSmallSlime();小的没有攻击力
*/
@Override
protected boolean canDamagePlayer()
{
return true;
}
/**
* @return 攻击强度,这里和岩浆怪保持一致,是史莱姆攻击强度加二
*/
@Override
protected int getAttackStrength()
{
return super.getAttackStrength() + 2;
}
/**
* 被攻击后发出的声音
* @return 这里和雪傀儡一致
*/
@Override
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
{
return SoundEvents.ENTITY_SNOWMAN_HURT;
}
/**
* 死亡音效
* @return 和雪傀儡一致
*/
@Override
protected SoundEvent getDeathSound()
{
return SoundEvents.ENTITY_SNOWMAN_DEATH;
}
/**
* 被挤压的音效
* @return 雪傀儡没有这个音效,所以这里用的是雪傀儡的环境音效
*/
@Override
protected SoundEvent getSquishSound()
{
return SoundEvents.ENTITY_SNOWMAN_AMBIENT;
}
/**
* 跳跃音效
* @return 使用扔雪球的音效
*/
protected SoundEvent getJumpSound()
{
return SoundEvents.ENTITY_SNOWBALL_THROW;
}
}
三、加载雪球怪的材质
做这一步时,我们需要下载两个模组,tabula和iChunUtil,这个我在上上传了,点这里下载tabula和iChunUtil。
具体如何使用这两个工具,并把模型导入项目,请参考b站的这个视频
然后做好材质以后我们就可以运行游戏了。下面是我做的材质:
四、运行游戏
呆萌的材质
能够被雨淋死,还能留下雪迹:
掉落物