如何正确地在持有某物品右键时对附近的某实体产生作用?

USS.Shenzhou


版本信息
你使用的IDE:IntelliJ IDEA
你使用的IDE版本:2020.1
Forge版本: 31.1.46
Minecraft版本: 1.15.2
Mapping 文件版本: 20200422-1.15.1

我尝试创建一个c4(ThrowableEntity)和对应的引爆器(Item),但是在最后阶段遇到了困难:我不知道如何正确的在拿着引爆器右键时使附近的c4爆炸。我在我的C4Entity的类中写好了与爆炸有关的方法exploder,并且尝试在c4_exploderonItemRightClick中使用它,就像这样:

然而第一个问题出现了:当我拿着它右键时,爆炸发生在玩家的手上而不是附近的c4实体。我大概意识到了我所犯的错误,但是不知道怎么样去修正它。
我尝试了另一种办法:我想要监听事件:玩家拿着物品时右键(PlayerInteractEvent.RightClickItem),且拿的是起爆器c4_exploder时,调用exploder使其爆炸: 但是我不知道如何正确的注册它(已阅读forge文档,我的问题在于这个监听器是写在c4entity.class而不是单独的类中的),也不知道是否会出现同第一个方法一样的错误。我该怎么做才能达到我想要的效果?
以下是c4_exploderc4entity的完整代码(在尝试第二种方法时):
c4_exploder:

public class c4_exploder extends Item {
    public c4_exploder(){
        super(new Properties().group(Utils.ItemGroup));
        this.setRegistryName("c4_exploder");
    }
    @Override
    public ActionResult<ItemStack> onItemRightClick(World worldIn, PlayerEntity playerIn, Hand handIn) {
        ItemStack itemstack = playerIn.getHeldItem(handIn);
        return new ActionResult<>(ActionResultType.SUCCESS, itemstack);
    }
}

c4entity

    public class c4entity extends ProjectileItemEntity {
    public c4entity(EntityType<? extends c4entity> type, LivingEntity playerIn, World worldIn) {
        super(type, playerIn, worldIn);
    }
    public c4entity(EntityType<c4entity> c4entityEntityType, World world) {
        super(c4entityEntityType, world);
    }

    @Nullable
    private BlockState inBlockState;

    public c4entity(LivingEntity throwerIn, World worldIn) {
        super(ModEntityTypes.c4entitytype,throwerIn, worldIn);
    }

    @Override
    protected Item getDefaultItem() {
        return ModItems.c4;
    }

    @Override
    protected void onImpact(RayTraceResult raytraceResultIn) {
        if (raytraceResultIn.getType() == RayTraceResult.Type.BLOCK) {
            BlockRayTraceResult blockraytraceresult = (BlockRayTraceResult) raytraceResultIn;
            BlockState blockstate = this.world.getBlockState(blockraytraceresult.getPos());
            this.inBlockState = blockstate;
            Vec3d vec3d = blockraytraceresult.getHitVec().subtract(this.getPosX(), this.getPosY(), this.getPosZ());
            this.setMotion(vec3d);
            Vec3d vec3d1 = vec3d.normalize().scale((double) 0.05F);
            this.prevPosX -= vec3d1.x;
            this.prevPosY -= vec3d1.y;
            this.prevPosZ -= vec3d1.z;
            this.playSound(ModSounds.c4_hit, 0.8f, 1.0f);
            this.inGround = true;
            this.setNoGravity(true);
            blockstate.onProjectileCollision(this.world, blockstate, blockraytraceresult, this);
        }
    }

    @Override
    public IPacket<?> createSpawnPacket() {
        return NetworkHooks.getEntitySpawningPacket(this);
    }


    private static final DataParameter<Boolean> IGNITED = EntityDataManager.createKey(CreeperEntity.class, DataSerializers.BOOLEAN);
    private static final DataParameter<Integer> STATE = EntityDataManager.createKey(CreeperEntity.class, DataSerializers.VARINT);
    private int fuseTime = 8;
    private int explosionRadius = 2;
    private int timeSinceIgnited = 1;

    @Override
    protected void registerData() {
        super.registerData();
        this.dataManager.register(STATE, -1);
        this.dataManager.register(IGNITED, false);
    }

    @Override
    public void writeAdditional(CompoundNBT compound) {
        super.writeAdditional(compound);
        compound.putShort("Fuse", (short) this.fuseTime);
        compound.putByte("ExplosionRadius", (byte) this.explosionRadius);
        compound.putBoolean("ignited", this.hasIgnited());
    }

    @Override
    public void readAdditional(CompoundNBT compound) {
        super.readAdditional(compound);
        if (compound.contains("Fuse", 99)) {
            this.fuseTime = compound.getShort("Fuse");
        }

        if (compound.contains("ExplosionRadius", 99)) {
            this.explosionRadius = compound.getByte("ExplosionRadius");
        }

        if (compound.getBoolean("ignited")) {
            this.ignite();
        }
    }

    public boolean hasIgnited() {
        return this.dataManager.get(IGNITED);
    }

    public void ignite() {
        this.dataManager.set(IGNITED, true);
    }

    public int getc4state() {
        return this.dataManager.get(STATE);
    }

    public void setc4state(int state) {
        this.dataManager.set(STATE, state);
    }

    private void explode() {
        if (!this.world.isRemote) {
            this.world.createExplosion(this, this.getPosX(), this.getPosY(), this.getPosZ(), (float) this.explosionRadius, Explosion.Mode.DESTROY);
            this.remove();
            this.spawnLingeringCloud();
        }
    }

    private void spawnLingeringCloud() {
        AreaEffectCloudEntity areaeffectcloudentity = new AreaEffectCloudEntity(this.world, this.getPosX(), this.getPosY(), this.getPosZ());
        areaeffectcloudentity.setRadius(1.0F);
        areaeffectcloudentity.setRadiusOnUse(-0.5F);
        areaeffectcloudentity.setWaitTime(10);
        areaeffectcloudentity.setDuration(areaeffectcloudentity.getDuration() / 2);
        areaeffectcloudentity.setRadiusPerTick(-areaeffectcloudentity.getRadius() / (float) areaeffectcloudentity.getDuration());
        this.world.addEntity(areaeffectcloudentity);
    }
    public void exploder() {
        this.ignite();
            this.setc4state(1);
            int i = this.getc4state();
            this.timeSinceIgnited += i;
            if (this.timeSinceIgnited < 0) {
                this.timeSinceIgnited = 0;
            }
            if (this.timeSinceIgnited >= this.fuseTime) {
                this.timeSinceIgnited = this.fuseTime;
                this.explode();
            }
    }
    @SubscribeEvent
    public void getexplode(PlayerInteractEvent.RightClickItem event){
        if(this.getThrower().getHeldItemMainhand().getItem()==ModItems.c4_exploder){
            this.exploder();
        }
    }

其中的延时爆炸功能还不太能起作用,不过这并不是重点。


FledgeXu


建议把explode改成public的。
World类下面有个叫做getEntitiesWithinAABB,可以通过这个方法获取周围的实体。
你可以在onItemRightClick里调用这个方法,获取周围有没有你想要的C4Entity,然后调用它的explode方法。


USS.Shenzhou


获取范围内的c4实体确实解决了


但是怎么才能让c4实体爆炸呢?现在是玩家在爆炸… 我知道应该不能简单地调用exploder 但是不知道具体怎么办…


FledgeXu


 public <T extends Entity> List<T> getEntitiesWithinAABB(@Nullable EntityType<T> type, AxisAlignedBB boundingBox, Predicate<? super T> predicate)

…你看看这个函数的签名,返回值是个List,你遍历一遍过滤出你的C4Entity不就行了……


USS.Shenzhou


不是检测范围的问题 是爆炸的问题 我想要的是右键之后旁边地上的c4entity爆炸 现在是我steve在爆炸。。。


FledgeXu


你先说清楚你的逻辑「什么叫做附近的Entity爆炸?」
你是要投出一个C4Entity,然后让他爆炸?


USS.Shenzhou


是的 :sweat:


FledgeXu


我还是不懂你的逻辑。你如果要右键引爆周围的C4Entity,那么周围的这些C4Entity是怎么来的?
我觉得你连整个运行逻辑都还没有弄清。


USS.Shenzhou


我的整个逻辑是:玩家丢出一个c4entity,然后拿着c4引爆器右键,使范围内的c4entity爆炸


FledgeXu


那你写错了,你list变量里装着的是C4Entity的实例,你应该让list变量里的元素爆炸而不是让exploder爆炸。

for(C4entity entity: list){
    entity.exploder();
}

类似这样写。


USS.Shenzhou


谢谢提醒!


system


该主题在最后一个回复创建后7天后自动关闭。不再允许新的回复。