Minecraft 1.12.2模组开发(四十五) 水火两用船 - 编程开发 - Minecraft(我的世界)中文论坛 -.html

Minecraft 1.12.2模组开发(四十五) 水火两用船 - 编程开发 - Minecraft(我的世界)中文论坛 -

Minecraft(我的世界)中文论坛

 找回密码
 注册(register)

!header_login!

只需一步,立刻登录

查看: 225|回复: 0

[Mod开发教程] Minecraft 1.12.2模组开发(四十五) 水火两用船

[复制链接]
我才是Joy 当前离线
积分
1660
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2014-4-13
查看详细资料
发表于 2022-5-9 13:34:36 | 显示全部楼层 |阅读模式

您尚未登录,立即登录享受更好的浏览体验!

您需要 登录 才可以下载或查看,没有帐号?注册(register)

x
本期教程之前可以先复习一下生物实体教程,会比较好理解一些。

今天我们在MC中实现一艘可以在水中和岩浆中滑行的船





![2022-05-09_13.21.29.png]()


1.船是一种生物实体,所以我们首先要制作船的模型,这里直接使用了原版的船模型,当然你也可以用[Blockbench](<a href="https://www.mcbbs.net/plugin.php?id=link_redirect&target=https%3A%2F%2Fblockbench.net%2F" target="_blank">https://blockbench.net/</a>)制作模型。


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中添加物品的贴图:




![cr4.jpg]()

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




![cr5.jpg]()

7.保存所有文件 -> 进入游戏测试:


岩浆中行船




![2022-05-09_13.21.29.png]()

水中行船




![2022-05-09_13.22.19.png]()


OK,大功告成!




您需要登录后才可以回帖 登录 | 注册(register)

本版积分规则

Archiver|小黑屋|Mcbbs.net ( 京ICP备15023768号-1 ) | 京公网安备 11010502037624号 | 手机版

GMT+8, 2022-6-18 14:05 , Processed in 0.109276 second(s), Total 14, Slave 12 queries, Release: Build.2022.05.30 1905, Gzip On, Redis On.

"Minecraft"以及"我的世界"为美国微软公司的商标 本站与微软公司没有从属关系

© 2010-2022 我的世界中文论坛 版权所有 本站内原创内容版权属于其原创作者,除作者或版规特别声明外未经许可不得转载