本期教程之前可以先复习一下生物实体教程,会比较好理解一些。
今天我们在MC中实现一艘可以在水中和岩浆中滑行的船

ModelObBoat.java
package com.joy187.rejoymod.entity.model;
import com.joy187.rejoymod.entity.EntityObBoat;
import net.minecraft.client.model.IMultipassModel;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.client.renderer.GLAllocation;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.MathHelper;
public class ModelObBoat extends ModelBase implements IMultipassModel
{
public ModelRenderer[] boatSides = new ModelRenderer[5];
public ModelRenderer[] paddles = new ModelRenderer[2];
public ModelRenderer noWater;
private final int patchList = GLAllocation.generateDisplayLists(1);
public ModelObBoat()
{
this.boatSides[0] = (new ModelRenderer(this, 0, 0)).setTextureSize(128, 64);
this.boatSides[1] = (new ModelRenderer(this, 0, 19)).setTextureSize(128, 64);
this.boatSides[2] = (new ModelRenderer(this, 0, 27)).setTextureSize(128, 64);
this.boatSides[3] = (new ModelRenderer(this, 0, 35)).setTextureSize(128, 64);
this.boatSides[4] = (new ModelRenderer(this, 0, 43)).setTextureSize(128, 64);
int i = 32;
int j = 6;
int k = 20;
int l = 4;
int i1 = 28;
this.boatSides[0].addBox(-14.0F, -9.0F, -3.0F, 28, 16, 3, 0.0F);
this.boatSides[0].setRotationPoint(0.0F, 3.0F, 1.0F);
this.boatSides[1].addBox(-13.0F, -7.0F, -1.0F, 18, 6, 2, 0.0F);
this.boatSides[1].setRotationPoint(-15.0F, 4.0F, 4.0F);
this.boatSides[2].addBox(-8.0F, -7.0F, -1.0F, 16, 6, 2, 0.0F);
this.boatSides[2].setRotationPoint(15.0F, 4.0F, 0.0F);
this.boatSides[3].addBox(-14.0F, -7.0F, -1.0F, 28, 6, 2, 0.0F);
this.boatSides[3].setRotationPoint(0.0F, 4.0F, -9.0F);
this.boatSides[4].addBox(-14.0F, -7.0F, -1.0F, 28, 6, 2, 0.0F);
this.boatSides[4].setRotationPoint(0.0F, 4.0F, 9.0F);
this.boatSides[0].rotateAngleX = ((float)Math.PI / 2F);
this.boatSides[1].rotateAngleY = ((float)Math.PI * 3F / 2F);
this.boatSides[2].rotateAngleY = ((float)Math.PI / 2F);
this.boatSides[3].rotateAngleY = (float)Math.PI;
this.paddles[0] = this.makePaddle(true);
this.paddles[0].setRotationPoint(3.0F, -5.0F, 9.0F);
this.paddles[1] = this.makePaddle(false);
this.paddles[1].setRotationPoint(3.0F, -5.0F, -9.0F);
this.paddles[1].rotateAngleY = (float)Math.PI;
this.paddles[0].rotateAngleZ = 0.19634955F;
this.paddles[1].rotateAngleZ = 0.19634955F;
this.noWater = (new ModelRenderer(this, 0, 0)).setTextureSize(128, 64);
this.noWater.addBox(-14.0F, -9.0F, -3.0F, 28, 16, 3, 0.0F);
this.noWater.setRotationPoint(0.0F, -3.0F, 1.0F);
this.noWater.rotateAngleX = ((float)Math.PI / 2F);
}
/**
* Sets the models various rotation angles then renders the model.
*/
public void render(Entity entityIn, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, float scale)
{
GlStateManager.rotate(90.0F, 0.0F, 1.0F, 0.0F);
EntityObBoat entityboat = (EntityObBoat)entityIn;
this.setRotationAngles(limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale, entityIn);
for (int i = 0; i < 5; ++i)
{
this.boatSides.render(scale);
}
this.renderPaddle(entityboat, 0, scale, limbSwing);
this.renderPaddle(entityboat, 1, scale, limbSwing);
}
public void renderMultipass(Entity p_187054_1_, float p_187054_2_, float p_187054_3_, float p_187054_4_, float p_187054_5_, float p_187054_6_, float scale)
{
GlStateManager.rotate(90.0F, 0.0F, 1.0F, 0.0F);
GlStateManager.colorMask(false, false, false, false);
this.noWater.render(scale);
GlStateManager.colorMask(true, true, true, true);
}
protected ModelRenderer makePaddle(boolean p_187056_1_)
{
ModelRenderer modelrenderer = (new ModelRenderer(this, 62, p_187056_1_ ? 0 : 20)).setTextureSize(128, 64);
int i = 20;
int j = 7;
int k = 6;
float f = -5.0F;
modelrenderer.addBox(-1.0F, 0.0F, -5.0F, 2, 2, 18);
modelrenderer.addBox(p_187056_1_ ? -1.001F : 0.001F, -3.0F, 8.0F, 1, 6, 7);
return modelrenderer;
}
protected void renderPaddle(EntityObBoat boat, int paddle, float scale, float limbSwing)
{
float f = boat.getRowingTime(paddle, limbSwing);
ModelRenderer modelrenderer = this.paddles[paddle];
modelrenderer.rotateAngleX = (float) MathHelper.clampedLerp(-1.0471975803375244D, -0.2617993950843811D, (double)((MathHelper.sin(-f) + 1.0F) / 2.0F));
modelrenderer.rotateAngleY = (float)MathHelper.clampedLerp(-(Math.PI / 4D), (Math.PI / 4D), (double)((MathHelper.sin(-f + 1.0F) + 1.0F) / 2.0F));
if (paddle == 1)
{
modelrenderer.rotateAngleY = (float)Math.PI - modelrenderer.rotateAngleY;
}
modelrenderer.render(scale);
}
}
2.我们要让船在岩浆和水上行走,所以我们的EntityObBoat 类要继承船实体类,同时让船对岩浆产生免疫:
EntityObBoat.java
package com.joy187.rejoymod.entity;
import com.joy187.rejoymod.item.ModItems;
import net.minecraft.block.BlockLiquid;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.MoverType;
import net.minecraft.entity.item.EntityBoat;
import net.minecraft.entity.passive.EntityAnimal;
import net.minecraft.entity.passive.EntityWaterMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.MobEffects;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.network.play.client.CPacketSteerBoat;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.util.ArrayList;
import java.util.List;
public class EntityObBoat extends EntityBoat {
private static final DataParameter<Integer> TIME_SINCE_HIT = EntityDataManager.createKey(EntityObBoat.class, DataSerializers.VARINT);
private static final DataParameter<Integer> FORWARD_DIRECTION = EntityDataManager.createKey(EntityObBoat.class, DataSerializers.VARINT);
private static final DataParameter<Float> DAMAGE_TAKEN = EntityDataManager.createKey(EntityObBoat.class, DataSerializers.FLOAT);
private static final DataParameter<Boolean>[] DATA_ID_PADDLE = new DataParameter[]{EntityDataManager.createKey(EntityObBoat.class, DataSerializers.BOOLEAN), EntityDataManager.createKey(EntityObBoat.class, DataSerializers.BOOLEAN)};
private final float[] paddlePositions;
private float outOfControlCounter;
private float deltaRotation;
private int lerpSteps;
private double boatPitch;
private double lerpY;
private double lerpZ;
private double boatYaw;
private double lerpXRot;
private boolean leftInputDown;
private boolean rightInputDown;
private boolean forwardInputDown;
private boolean backInputDown;
private double lavaLevel;
private float boatGlide;
private Status status;
private Status previousStatus;
private double lastYd;
public EntityObBoat(World world)
{
super(world);
//我们的船对岩浆、火免疫,不然会烧掉
this.isImmuneToFire = true;
this.paddlePositions = new float[2];
this.preventEntitySpawning = true;
this.setSize(1.375F, 0.5625F);
}
public EntityObBoat(World world, double x, double y, double z)
{
this(world);
this.motionX = 0.0D;
this.motionY = 0.0D;
this.motionZ = 0.0D;
this.prevPosX = x;
this.prevPosY = y;
this.prevPosZ = z;
this.setPosition(x, y, z);
}
@Override
protected boolean canTriggerWalking()
{
return false;
}
@Override
protected void entityInit()
{
this.dataManager.register(TIME_SINCE_HIT, 0);
this.dataManager.register(FORWARD_DIRECTION, 1);
this.dataManager.register(DAMAGE_TAKEN, 0.0F);
for(DataParameter<Boolean> parameter : DATA_ID_PADDLE)
{
this.dataManager.register(parameter, false);
}
}
@Override
public AxisAlignedBB getCollisionBox(Entity entity)
{
return entity.canBePushed() ? entity.getEntityBoundingBox() : null;
}
@Override
public AxisAlignedBB getCollisionBoundingBox()
{
return this.getEntityBoundingBox();
}
@Override
public boolean canBePushed()
{
return true;
}
@Override
public double getMountedYOffset()
{
return -0.1D;
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount)
{
if(this.isEntityInvulnerable(source))
{
return false;
}
else if(!this.world.isRemote && !this.isDead)
{
if(source instanceof EntityDamageSourceIndirect && source.getTrueSource() != null && this.isPassenger(source.getTrueSource()))
{
return false;
}
else
{
this.setForwardDirection(-this.getForwardDirection());
this.setTimeSinceHit(10);
this.setDamageTaken(this.getDamageTaken() + amount * 10.0F);
this.markVelocityChanged();
boolean takeDamage = source.getTrueSource() instanceof EntityPlayer && ((EntityPlayer) source.getTrueSource()).capabilities.isCreativeMode;
if(takeDamage || this.getDamageTaken() > 40.0F)
{
//判断我们如果将船打破后会掉落什么物品
if(!takeDamage && this.world.getGameRules().getBoolean("doEntityDrops"))
{
this.dropItemWithOffset(ModItems.OBBOAT, 1, 0.0F);
}
this.setDead();
}
return true;
}
}
else
{
return true;
}
}
@Override
public void applyEntityCollision(Entity entity)
{
if(entity instanceof EntityObBoat)
{
if(entity.getEntityBoundingBox().minY < this.getEntityBoundingBox().maxY)
{
super.applyEntityCollision(entity);
}
}
else if(entity.getEntityBoundingBox().minY <= this.getEntityBoundingBox().minY)
{
super.applyEntityCollision(entity);
}
}
@Override
@SideOnly(Side.CLIENT)
public void performHurtAnimation()
{
this.setForwardDirection(-this.getForwardDirection());
this.setTimeSinceHit(10);
this.setDamageTaken(this.getDamageTaken() * 11.0F);
}
@Override
public boolean canBeCollidedWith()
{
return !this.isDead;
}
@Override
@SideOnly(Side.CLIENT)
public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch, int posRotationIncrements, boolean teleport)
{
this.boatPitch = x;
this.lerpY = y;
this.lerpZ = z;
this.boatYaw = yaw;
this.lerpXRot = pitch;
this.lerpSteps = 10;
}
@Override
public EnumFacing getAdjustedHorizontalFacing()
{
return this.getHorizontalFacing().rotateY();
}
@Override
public void onUpdate()
{
this.previousStatus = this.status;
this.status = this.getBoatStatus();
if(this.status != Status.UNDER_LAVA && this.status != Status.UNDER_FLOWING_LAVA)
{
this.outOfControlCounter = 0.0F;
}
else
{
this.outOfControlCounter++;
}
if(!this.world.isRemote && this.outOfControlCounter >= 60.0F)
{
this.removePassengers();
}
if(this.getTimeSinceHit() > 0)
{
this.setTimeSinceHit(this.getTimeSinceHit() - 1);
}
if(this.getDamageTaken() > 0.0F)
{
this.setDamageTaken(this.getDamageTaken() - 1.0F);
}
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;
if(!this.world.isRemote)
{
this.setFlag(6, this.isGlowing());
}
this.onEntityUpdate();
this.tickLerp();
if(this.canPassengerSteer())
{
if(this.getPassengers().size() == 0 || !(this.getPassengers().get(0) instanceof EntityPlayer))
{
this.setPaddleState(false, false);
}
this.updateMotion();
if(this.world.isRemote)
{
this.controlBoat();
this.world.sendPacketToServer(new CPacketSteerBoat(this.getPaddleState(0), this.getPaddleState(1)));
}
this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ);
}
else
{
this.motionX = 0.0D;
this.motionY = 0.0D;
this.motionZ = 0.0D;
}
for(int i = 0; i <= 1; i++)
{
if(this.getPaddleState(i))
{
if(!this.isSilent() && (double) (this.paddlePositions % ((float) Math.PI * 2F)) <= (Math.PI / 4D) && ((double) this.paddlePositions + 0.39269909262657166D) % (Math.PI * 2D) >= (Math.PI / 4D))
{
SoundEvent event = this.getPaddleSound();
if(event != null)
{
Vec3d look = this.getLook(1.0F);
double lookX = i == 1 ? -look.z : look.z;
double lookZ = i == 1 ? look.x : -look.x;
this.world.playSound(null, this.posX + lookX, this.posY, this.posZ + lookZ, event, this.getSoundCategory(), 1.0F, 0.8F + 0.4F * this.rand.nextFloat());
}
}
this.paddlePositions = (float) ((double) this.paddlePositions + 0.39269909262657166D);
}
else
{
this.paddlePositions = 0.0F;
}
}
this.doBlockCollisions();
List<Entity> entities = this.world.getEntitiesInAABBexcluding(this, this.getEntityBoundingBox().expand(0.20000000298023224D, -0.009999999776482582D, 0.20000000298023224D), EntitySelectors.getTeamCollisionPredicate(this));
if(!entities.isEmpty())
{
boolean flag = !this.world.isRemote && !(this.getControllingPassenger() instanceof EntityPlayer);
for(Entity entity : entities)
{
if(!entity.isPassenger(this))
{
if(flag && this.getPassengers().size() < 2 && !entity.isRiding() && entity.width < this.width && entity instanceof EntityLivingBase && !(entity instanceof EntityWaterMob) && !(entity instanceof EntityPlayer))
{
entity.startRiding(this);
}
else
{
this.applyEntityCollision(entity);
}
}
}
}
}
//船被打破之后掉落的物品
@Override
public ItemStack getPickedResult(RayTraceResult target)
{
return new ItemStack(ModItems.OBBOAT, 1, 0);
}
private void tickLerp()
{
if(this.lerpSteps > 0 && !this.canPassengerSteer())
{
double posX = this.posX + (this.boatPitch - this.posX) / (double) this.lerpSteps;
double posY = this.posY + (this.lerpY - this.posY) / (double) this.lerpSteps;
double posZ = this.posZ + (this.lerpZ - this.posZ) / (double) this.lerpSteps;
double yaw = MathHelper.wrapDegrees(this.boatYaw - (double) this.rotationYaw);
this.rotationYaw = (float) ((double) this.rotationYaw + yaw / (double) this.lerpSteps);
this.rotationPitch = (float) ((double) this.rotationPitch + (this.lerpXRot - (double) this.rotationPitch) / (double) this.lerpSteps);
this.lerpSteps--;
this.setPosition(posX, posY, posZ);
this.setRotation(this.rotationYaw, this.rotationPitch);
}
}
@Override
public void setPaddleState(boolean leftPaddle, boolean rightPaddle)
{
this.dataManager.set(DATA_ID_PADDLE[0], leftPaddle);
this.dataManager.set(DATA_ID_PADDLE[1], rightPaddle);
}
@Override
@SideOnly(Side.CLIENT)
public float getRowingTime(int side, float limbSwing)
{
return this.getPaddleState(side) ? (float) MathHelper.clampedLerp((double) this.paddlePositions[side] - 0.39269909262657166D, this.paddlePositions[side], limbSwing) : 0.0F;
}
private Status getBoatStatus()
{
Status status = this.getUnderLavaStatus();
if(status != null)
{
this.lavaLevel = this.getEntityBoundingBox().maxY;
return status;
}
else if(this.checkInLava())
{
return Status.IN_LAVA;
}
else
{
float f = this.getBoatGlide();
if(f > 0.0F)
{
this.boatGlide = f;
return Status.ON_LAND;
}
else
{
return Status.IN_AIR;
}
}
}
private float getLavaLevelAbove()
{
AxisAlignedBB boundingBox = this.getEntityBoundingBox();
int minX = MathHelper.floor(boundingBox.minX);
int maxX = MathHelper.ceil(boundingBox.maxX);
int minY = MathHelper.floor(boundingBox.maxY);
int maxY = MathHelper.ceil(boundingBox.maxY - this.lastYd);
int minZ = MathHelper.floor(boundingBox.minZ);
int maxZ = MathHelper.ceil(boundingBox.maxZ);
BlockPos.PooledMutableBlockPos mutableBlockPos = BlockPos.PooledMutableBlockPos.retain();
try
{
label78:
for(int y = minY; y < maxY; y++)
{
float liquidHeight = 0.0F;
int x = minX;
while(true)
{
if(x >= maxX)
{
if(liquidHeight < 1.0F)
{
return (float) mutableBlockPos.getY() + liquidHeight;
}
break;
}
for(int z = minZ; z < maxZ; z++)
{
mutableBlockPos.setPos(x, y, z);
IBlockState state = this.world.getBlockState(mutableBlockPos);
//判断船是否在岩浆或者水上
if(state.getMaterial() == Material.LAVA || state.getMaterial() == Material.WATER)
{
liquidHeight = Math.max(liquidHeight, BlockLiquid.getBlockLiquidHeight(state, this.world, mutableBlockPos));
}
if(liquidHeight >= 1.0F)
{
continue label78;
}
}
x++;
}
}
return (float) (maxY + 1);
}
finally
{
mutableBlockPos.release();
}
}
@Override
public float getBoatGlide()
{
AxisAlignedBB entityBoundingBox = this.getEntityBoundingBox();
AxisAlignedBB boundingBox = new AxisAlignedBB(entityBoundingBox.minX, entityBoundingBox.minY - 0.001D, entityBoundingBox.minZ, entityBoundingBox.maxX, entityBoundingBox.minY, entityBoundingBox.maxZ);
int minX = MathHelper.floor(boundingBox.minX) - 1;
int maxX = MathHelper.ceil(boundingBox.maxX) + 1;
int minY = MathHelper.floor(boundingBox.minY) - 1;
int maxY = MathHelper.ceil(boundingBox.maxY) + 1;
int minZ = MathHelper.floor(boundingBox.minZ) - 1;
int maxZ = MathHelper.ceil(boundingBox.maxZ) + 1;
List<AxisAlignedBB> list = new ArrayList<>();
float slipperiness = 0.0F;
int friction = 0;
BlockPos.PooledMutableBlockPos mutableBlockPos = BlockPos.PooledMutableBlockPos.retain();
try
{
for(int x = minX; x < maxX; x++)
{
for(int z = minZ; z < maxZ; z++)
{
int offset = (x != minX && x != maxX - 1 ? 0 : 1) + (z != minZ && z != maxZ - 1 ? 0 : 1);
if(offset != 2)
{
for(int y = minY; y < maxY; y++)
{
if(offset <= 0 || y != minY && y != maxY - 1)
{
mutableBlockPos.setPos(x, y, z);
IBlockState state = this.world.getBlockState(mutableBlockPos);
state.addCollisionBoxToList(this.world, mutableBlockPos, boundingBox, list, this, false);
if(!list.isEmpty())
{
slipperiness += state.getBlock().getSlipperiness(state, this.world, mutableBlockPos, this);
friction++;
}
list.clear();
}
}
}
}
}
}
finally
{
mutableBlockPos.release();
}
return slipperiness / (float) friction;
}
private boolean checkInLava()
{
AxisAlignedBB entityBoundingBox = this.getEntityBoundingBox();
int minX = MathHelper.floor(entityBoundingBox.minX);
int maxX = MathHelper.ceil(entityBoundingBox.maxX);
int minY = MathHelper.floor(entityBoundingBox.minY);
int maxY = MathHelper.ceil(entityBoundingBox.minY + 0.001D);
int minZ = MathHelper.floor(entityBoundingBox.minZ);
int maxZ = MathHelper.ceil(entityBoundingBox.maxZ);
boolean flag = false;
this.lavaLevel = Double.MIN_VALUE;
BlockPos.PooledMutableBlockPos mutableBlockPos = BlockPos.PooledMutableBlockPos.retain();
try
{
for(int x = minX; x < maxX; x++)
{
for(int y = minY; y < maxY; y++)
{
for(int z = minZ; z < maxZ; z++)
{
mutableBlockPos.setPos(x, y, z);
IBlockState state = this.world.getBlockState(mutableBlockPos);
//判断岩浆是否在岩浆或水中滑行
if(state.getMaterial() == Material.LAVA || state.getMaterial() == Material.WATER)
{
float liquidHeight = BlockLiquid.getLiquidHeight(state, this.world, mutableBlockPos);
this.lavaLevel = Math.max(liquidHeight, this.lavaLevel);
flag |= entityBoundingBox.minY < (double) liquidHeight;
}
}
}
}
}
finally
{
mutableBlockPos.release();
}
return flag;
}
private Status getUnderLavaStatus()
{
AxisAlignedBB entityBoundingBox = this.getEntityBoundingBox();
double entityHeight = entityBoundingBox.maxY + 0.001D;
int minX = MathHelper.floor(entityBoundingBox.minX);
int maxX = MathHelper.ceil(entityBoundingBox.maxX);
int minY = MathHelper.floor(entityBoundingBox.maxY);
int maxY = MathHelper.ceil(entityHeight);
int minZ = MathHelper.floor(entityBoundingBox.minZ);
int maxZ = MathHelper.ceil(entityBoundingBox.maxZ);
boolean flag = false;
BlockPos.PooledMutableBlockPos mutableBlockPos = BlockPos.PooledMutableBlockPos.retain();
try
{
for(int x = minX; x < maxX; x++)
{
for(int y = minY; y < maxY; y++)
{
for(int z = minZ; z < maxZ; z++)
{
mutableBlockPos.setPos(x, y, z);
IBlockState state = this.world.getBlockState(mutableBlockPos);
if((state.getMaterial() == Material.LAVA || state.getMaterial() == Material.WATER) && entityHeight < (double) BlockLiquid.getLiquidHeight(state, this.world, mutableBlockPos))
{
if(state.getValue(BlockLiquid.LEVEL) != 0)
{
return Status.UNDER_FLOWING_LAVA;
}
flag = true;
}
}
}
}
}
finally
{
mutableBlockPos.release();
}
return flag ? Status.UNDER_LAVA : null;
}
private void updateMotion()
{
double gravity = this.hasNoGravity() ? 0.0D : -0.03999999910593033D;
double position = 0.0D;
float momentum = 0.05F;
if(this.previousStatus == Status.IN_AIR && this.status != Status.IN_AIR && this.status != Status.ON_LAND)
{
this.lavaLevel = this.getEntityBoundingBox().minY + (double) this.height;
this.setPosition(this.posX, (double) (this.getLavaLevelAbove() - this.height) + 0.101D, this.posZ);
this.motionY = 0.0D;
this.lastYd = 0.0D;
this.status = Status.IN_LAVA;
}
else
{
if(this.status == Status.IN_LAVA)
{
position = (this.lavaLevel - this.getEntityBoundingBox().minY) / (double) this.height;
momentum = 0.9F;
}
else if(this.status == Status.UNDER_FLOWING_LAVA)
{
gravity = -7.0E-4D;
momentum = 0.9F;
}
else if(this.status == Status.UNDER_LAVA)
{
position = 0.009999999776482582D;
momentum = 0.45F;
}
else if(this.status == Status.IN_AIR)
{
momentum = 0.9F;
}
else if(this.status == Status.ON_LAND)
{
momentum = this.boatGlide;
if(this.getControllingPassenger() instanceof EntityPlayer)
{
this.boatGlide /= 2.0F;
}
}
this.motionX *= momentum;
this.motionZ *= momentum;
this.deltaRotation *= momentum;
this.motionY += gravity;
if(position > 0.0D)
{
this.motionY += position * 0.06153846016296973D;
this.motionY *= 0.75D;
}
}
}
private void controlBoat()
{
if(this.isBeingRidden())
{
float speed = 0.0F;
if(this.leftInputDown)
{
this.deltaRotation += -1.0F;
}
if(this.rightInputDown)
{
this.deltaRotation++;
}
if(this.rightInputDown != this.leftInputDown && !this.forwardInputDown && !this.backInputDown)
{
speed += 0.005F;
}
this.rotationYaw += this.deltaRotation;
if(this.forwardInputDown)
{
speed += 0.04F;
}
if(this.backInputDown)
{
speed -= 0.005F;
}
this.motionX += (MathHelper.sin(-this.rotationYaw * 0.017453292F) * speed);
this.motionZ += (MathHelper.cos(this.rotationYaw * 0.017453292F) * speed);
this.setPaddleState(this.rightInputDown && !this.leftInputDown || this.forwardInputDown, this.leftInputDown && !this.rightInputDown || this.forwardInputDown);
}
}
@Override
public void updatePassenger(Entity passenger)
{
//当玩家进入船中,防止其着火,就赋予其防火效果
((EntityLivingBase)passenger).addPotionEffect(new PotionEffect(MobEffects.FIRE_RESISTANCE, 100));
if(this.isPassenger(passenger))
{
float x = 0.0F;
float height = (float) ((this.isDead ? 0.009999999776482582D : this.getMountedYOffset()) + passenger.getYOffset());
if(this.getPassengers().size() > 1)
{
int index = this.getPassengers().indexOf(passenger);
if(index == 0)
{
x = 0.2F;
}
else
{
x = -0.6F;
}
if(passenger instanceof EntityAnimal)
{
x = (float) ((double) x + 0.2D);
}
}
Vec3d pos = (new Vec3d(x, 0.0D, 0.0D)).rotateYaw(-this.rotationYaw * 0.017453292F - ((float) Math.PI / 2F));
passenger.setPosition(this.posX + pos.x, this.posY + (double) height, this.posZ + pos.z);
passenger.rotationYaw += this.deltaRotation;
passenger.setRotationYawHead(passenger.getRotationYawHead() + this.deltaRotation);
this.applyYawToEntity(passenger);
if(passenger instanceof EntityAnimal && this.getPassengers().size() > 1)
{
int rotation = passenger.getEntityId() % 2 == 0 ? 90 : 270;
passenger.setRenderYawOffset(((EntityAnimal) passenger).renderYawOffset + (float) rotation);
passenger.setRotationYawHead(passenger.getRotationYawHead() + (float) rotation);
}
if(this.isInsideOfMaterial(Material.WATER))
{
passenger.dismountRidingEntity();
}
}
}
@Override
protected void applyYawToEntity(Entity entityToUpdate)
{
entityToUpdate.setRenderYawOffset(this.rotationYaw);
float yaw = MathHelper.wrapDegrees(entityToUpdate.rotationYaw - this.rotationYaw);
float clampedYaw = MathHelper.clamp(yaw, -105.0F, 105.0F);
entityToUpdate.prevRotationYaw += clampedYaw - yaw;
entityToUpdate.rotationYaw += clampedYaw - yaw;
entityToUpdate.setRotationYawHead(entityToUpdate.rotationYaw);
}
@Override
@SideOnly(Side.CLIENT)
public void applyOrientationToEntity(Entity entityToUpdate)
{
this.applyYawToEntity(entityToUpdate);
}
@Override
protected void writeEntityToNBT(NBTTagCompound compound)
{
}
@Override
protected void readEntityFromNBT(NBTTagCompound compound)
{
}
@Override
public boolean processInitialInteract(EntityPlayer player, EnumHand hand)
{
if(player.isSneaking())
{
return false;
}
else
{
if(!this.world.isRemote && this.outOfControlCounter < 60.0F && !this.isInsideOfMaterial(Material.WATER))
{
player.startRiding(this);
}
return true;
}
}
@Override
protected void updateFallState(double y, boolean onGround, IBlockState state, BlockPos pos)
{
this.lastYd = this.motionY;
if(!this.isRiding())
{
if(onGround)
{
if(this.fallDistance > 3.0F)
{
if(this.status != Status.ON_LAND)
{
this.fallDistance = 0.0F;
return;
}
this.fall(this.fallDistance, 1.0F);
if(!this.world.isRemote && !this.isDead)
{
this.setDead();
if(this.world.getGameRules().getBoolean("doEntityDrops"))
{
for(int i = 0; i < 3; i++)
{
this.entityDropItem(new ItemStack(Blocks.OBSIDIAN, 1, 0), 0.0F);
}
}
}
}
this.fallDistance = 0.0F;
}
else if(this.world.getBlockState((new BlockPos(this)).down()).getMaterial() != Material.LAVA && y < 0.0D ||
this.world.getBlockState((new BlockPos(this)).down()).getMaterial() != Material.WATER && y < 0.0D)
{
this.fallDistance = (float) ((double) this.fallDistance - y);
}
}
}
@Override
public boolean getPaddleState(int state)
{
return this.dataManager.get(DATA_ID_PADDLE[state]) && this.getControllingPassenger() != null;
}
@Override
public void setDamageTaken(float damageTaken)
{
this.dataManager.set(DAMAGE_TAKEN, damageTaken);
}
@Override
public float getDamageTaken()
{
return this.dataManager.get(DAMAGE_TAKEN);
}
@Override
public void setTimeSinceHit(int timeSinceHit)
{
this.dataManager.set(TIME_SINCE_HIT, timeSinceHit);
}
@Override
public int getTimeSinceHit()
{
return this.dataManager.get(TIME_SINCE_HIT);
}
@Override
public void setForwardDirection(int forwardDirection)
{
this.dataManager.set(FORWARD_DIRECTION, forwardDirection);
}
@Override
public int getForwardDirection()
{
return this.dataManager.get(FORWARD_DIRECTION);
}
@Override
protected boolean canFitPassenger(Entity passenger)
{
return this.getPassengers().size() < 2;
}
@Override
public Entity getControllingPassenger()
{
List<Entity> passengers = this.getPassengers();
return passengers.isEmpty() ? null : passengers.get(0);
}
@Override
@SideOnly(Side.CLIENT)
public void updateInputs(boolean left, boolean right, boolean forward, boolean back)
{
this.leftInputDown = left;
this.rightInputDown = right;
this.forwardInputDown = forward;
this.backInputDown = back;
}
//枚举船的几种状态
public enum Status
{
IN_LAVA,
UNDER_LAVA,
UNDER_FLOWING_LAVA,
ON_LAND,
IN_AIR
}
}
3.新建船的渲染类RenderObBoat.java ,这里直接使用原版的船渲染类。
RenderObBoat.java
package com.joy187.rejoymod.entity.render;
import com.joy187.rejoymod.entity.EntityObBoat;
import com.joy187.rejoymod.entity.model.ModelObBoat;
import com.joy187.rejoymod.util.Reference;
import net.minecraft.client.model.IMultipassModel;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
public class RenderObBoat extends Render<EntityObBoat> {
//这个要对应我们的船的皮肤贴图文件路径
public static final ResourceLocation TEXTURES = new ResourceLocation(Reference.Mod_ID + ":textures/entity/obboat.png");
protected ModelBase modelBoat = new ModelObBoat();
public RenderObBoat(RenderManager renderManagerIn)
{
super(renderManagerIn);
this.shadowSize = 0.5F;
this.modelBoat = new ModelObBoat();
}
public void doRender(EntityObBoat entity, double x, double y, double z, float entityYaw, float partialTicks)
{
GlStateManager.pushMatrix();
this.setupTranslation(x, y, z);
this.setupRotation(entity, entityYaw, partialTicks);
this.bindEntityTexture(entity);
if (this.renderOutlines)
{
GlStateManager.enableColorMaterial();
GlStateManager.enableOutlineMode(this.getTeamColor(entity));
}
this.modelBoat.render(entity, partialTicks, 0.0F, -0.1F, 0.0F, 0.0F, 0.0625F);
if (this.renderOutlines)
{
GlStateManager.disableOutlineMode();
GlStateManager.disableColorMaterial();
}
GlStateManager.popMatrix();
super.doRender(entity, x, y, z, entityYaw, partialTicks);
}
public void setupRotation(EntityObBoat p_188311_1_, float p_188311_2_, float p_188311_3_)
{
GlStateManager.rotate(180.0F - p_188311_2_, 0.0F, 1.0F, 0.0F);
float f = (float)p_188311_1_.getTimeSinceHit() - p_188311_3_;
float f1 = p_188311_1_.getDamageTaken() - p_188311_3_;
if (f1 < 0.0F)
{
f1 = 0.0F;
}
if (f > 0.0F)
{
GlStateManager.rotate(MathHelper.sin(f) * f * f1 / 10.0F * (float)p_188311_1_.getForwardDirection(), 1.0F, 0.0F, 0.0F);
}
GlStateManager.scale(-1.0F, -1.0F, 1.0F);
}
public void setupTranslation(double p_188309_1_, double p_188309_3_, double p_188309_5_)
{
GlStateManager.translate((float)p_188309_1_, (float)p_188309_3_ + 0.375F, (float)p_188309_5_);
}
/**
* Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture.
*/
protected ResourceLocation getEntityTexture(EntityObBoat entity){
return TEXTURES;
}
public boolean isMultipass()
{
return true;
}
public void renderMultipass(EntityObBoat p_188300_1_, double p_188300_2_, double p_188300_4_, double p_188300_6_, float p_188300_8_, float p_188300_9_)
{
GlStateManager.pushMatrix();
this.setupTranslation(p_188300_2_, p_188300_4_, p_188300_6_);
this.setupRotation(p_188300_1_, p_188300_8_, p_188300_9_);
this.bindEntityTexture(p_188300_1_);
((IMultipassModel)this.modelBoat).renderMultipass(p_188300_1_, p_188300_9_, 0.0F, -0.1F, 0.0F, 0.0F, 0.0625F);
GlStateManager.popMatrix();
}
}
在RenderHandler.java 中的registerEntityRenders()中添加船的渲染注册:
RenderHandler.java
public class RenderHandler {
public static void registerEntityRenders() {
RenderingRegistry.registerEntityRenderingHandler(EntityObBoat.class, new IRenderFactory<EntityObBoat>()
{
@Override
public Render<? super EntityObBoat> createRenderFor(RenderManager manager)
{
return new RenderObBoat(manager);
}
});
}
}
4.在EntityInit.java 中添加我们的船的注册信息:
registerEntity("obboat", EntityObBoat.class, ENTITY_NEXT_ID, 20);
5.生物实体部分完成,我们要制作一个专门生成船的物品,新建ItemObBoat 类。
ItemObBoat.java
package com.joy187.rejoymod.item;
import com.joy187.rejoymod.entity.EntityObBoat;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.stats.StatList;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import java.util.List;
public class ItemObBoat extends ItemBase{
public ItemObBoat(String name, CreativeTabs tab) {
super(name,tab);
this.maxStackSize=1;
}
private EntityObBoat.Type type= EntityObBoat.Type.OAK;
public ItemObBoat(EntityObBoat.Type typeIn)
{
this.type = typeIn;
this.maxStackSize = 1;
this.setCreativeTab(CreativeTabs.TRANSPORTATION);
this.setUnlocalizedName("boat." + "ob");
}
/**
* Called when the equipped item is right clicked.
*/
public ActionResult<ItemStack> onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand handIn)
{
ItemStack itemstack = playerIn.getHeldItem(handIn);
float f = 1.0F;
float f1 = playerIn.prevRotationPitch + (playerIn.rotationPitch - playerIn.prevRotationPitch) * 1.0F;
float f2 = playerIn.prevRotationYaw + (playerIn.rotationYaw - playerIn.prevRotationYaw) * 1.0F;
double d0 = playerIn.prevPosX + (playerIn.posX - playerIn.prevPosX) * 1.0D;
double d1 = playerIn.prevPosY + (playerIn.posY - playerIn.prevPosY) * 1.0D + (double)playerIn.getEyeHeight();
double d2 = playerIn.prevPosZ + (playerIn.posZ - playerIn.prevPosZ) * 1.0D;
Vec3d vec3d = new Vec3d(d0, d1, d2);
float f3 = MathHelper.cos(-f2 * 0.017453292F - (float)Math.PI);
float f4 = MathHelper.sin(-f2 * 0.017453292F - (float)Math.PI);
float f5 = -MathHelper.cos(-f1 * 0.017453292F);
float f6 = MathHelper.sin(-f1 * 0.017453292F);
float f7 = f4 * f5;
float f8 = f3 * f5;
double d3 = 5.0D;
Vec3d vec3d1 = vec3d.addVector((double)f7 * 5.0D, (double)f6 * 5.0D, (double)f8 * 5.0D);
RayTraceResult raytraceresult = worldIn.rayTraceBlocks(vec3d, vec3d1, true);
if (raytraceresult == null)
{
return new ActionResult<ItemStack>(EnumActionResult.PASS, itemstack);
}
else
{
Vec3d vec3d2 = playerIn.getLook(1.0F);
boolean flag = false;
List<Entity> list = worldIn.getEntitiesWithinAABBExcludingEntity(playerIn, playerIn.getEntityBoundingBox().expand(vec3d2.x * 5.0D, vec3d2.y * 5.0D, vec3d2.z * 5.0D).grow(1.0D));
for (int i = 0; i < list.size(); ++i)
{
Entity entity = list.get(i);
if (entity.canBeCollidedWith())
{
AxisAlignedBB axisalignedbb = entity.getEntityBoundingBox().grow((double)entity.getCollisionBorderSize());
if (axisalignedbb.contains(vec3d))
{
flag = true;
}
}
}
if (flag)
{
return new ActionResult<ItemStack>(EnumActionResult.PASS, itemstack);
}
else if (raytraceresult.typeOfHit != RayTraceResult.Type.BLOCK)
{
return new ActionResult<ItemStack>(EnumActionResult.PASS, itemstack);
}
else
{
Block block = worldIn.getBlockState(raytraceresult.getBlockPos()).getBlock();
boolean flag1 = block == Blocks.WATER || block == Blocks.FLOWING_WATER || block == Blocks.LAVA || block == Blocks.FLOWING_LAVA;
//这里声明我们要召唤的船的实体类(第2步中的实体类)
EntityObBoat entityboat = new EntityObBoat(worldIn, raytraceresult.hitVec.x, flag1 ? raytraceresult.hitVec.y - 0.12D : raytraceresult.hitVec.y, raytraceresult.hitVec.z);
//entityboat.setBoatType(this.type);
entityboat.rotationYaw = playerIn.rotationYaw;
if (!worldIn.getCollisionBoxes(entityboat, entityboat.getEntityBoundingBox().grow(-0.1D)).isEmpty())
{
return new ActionResult<ItemStack>(EnumActionResult.FAIL, itemstack);
}
else
{
if (!worldIn.isRemote)
{
worldIn.spawnEntity(entityboat);
}
if (!playerIn.capabilities.isCreativeMode)
{
itemstack.shrink(1);
}
playerIn.addStat(StatList.getObjectUseStats(this));
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, itemstack);
}
}
}
}
}
在ModItems.java 中添加该物品的注册信息:
//物品名称 放入哪个物品栏
public static final Item OBBOAT = new ItemObBoat("obboat",IdlFramework.ITEM_TAB);
6.在resources包中的assets\rejoymod\models\item 中新建物品模型文件:
obboat.json
{
"parent": "item/generated",
"textures": {
"layer0": "rejoymod:items/obboat"
}
}
在en_us.lang 中添加船物品和船实体的英文名称:
item.obboat.name=Example Boat
entity.obboat.name=Example Boat
在textures\item 中添加物品的贴图:

在textures\entity 中添加船实体的皮肤贴图:

7.保存所有文件 -> 进入游戏测试:
岩浆中行船

水中行船

OK,大功告成!
|