You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Fabric 1.21.11:GeckoLib CannonSeatEntity碰撞盒与交互优化求助

Fabric 1.21.11大炮模组:座椅实体碰撞与交互优化方案

开发背景

基于Fabric 1.21.11开发Minecraft模组,包含一款使用自定义GeckoLib(5.4.4)BlockEntity模型的大炮方块。点击大炮时会生成独立的隐形座椅实体CannonSeatEntity(继承自Entity),用于实现玩家骑乘功能。

当前状态

  • 已通过Mixin移除实体白色轮廓线
  • 实体仍存在1x1x1的隐形碰撞盒,玩家无法穿过

核心问题

CannonSeatEntity始终以完整立方体碰撞盒阻挡玩家移动,不符合设计需求。

已尝试无效方案

  • 重写getVisualShape()返回Shapes.empty()
  • 重写getDimensions()getBoundingBox()为final方法无法修改)
  • LevelRenderer使用Mixin修改轮廓渲染
  • 设置setInvisible(true)
  • 开启noPhysics = true
  • 重写isPickable()返回false
  • EntityType.Builder中设置极小尺寸

需求目标

  1. 完全移除座椅实体的物理碰撞盒,不阻挡玩家移动
  2. 保留玩家骑乘大炮的功能
  3. 创建匹配自定义3D GeckoLib模型的非立方体VoxelShape
  4. 实现大炮模型全域可点击,点击任意位置均可触发骑乘

解决方案

1. 彻底移除座椅实体碰撞

CannonSeatEntity中重写以下核心方法:

// 禁止实体被推动
@Override
public boolean isPushable() {
    return false;
}

// 设置极小的剔除包围盒
@Override
protected AABB getBoundingBoxForCulling() {
    return AABB.ofSize(this.position(), 0.0, 0.0, 0.0);
}

// 返回空碰撞形状
@Override
public VoxelShape getCollisionShape(EntityCollisionContext context) {
    return Shapes.empty();
}

// 拒绝与任何实体发生碰撞
@Override
public boolean canCollideWith(Entity other) {
    return false;
}

同时在构建实体类型时,强制设置极小尺寸:

EntityType.Builder.<CannonSeatEntity>of(CannonSeatEntity::new, EntityCategory.MISC)
    .setDimensions(0.0F, 0.0F)
    .setTrackingRange(10)
    .setUpdateInterval(20)
    .build("cannon_seat");

2. 保留骑乘功能的直接绑定

座椅实体无需依赖碰撞盒触发骑乘,在点击大炮时直接调用玩家的骑乘方法:

// 点击大炮时的触发逻辑
public void triggerRide(Player player, BlockPos pos) {
    Level level = player.level();
    if (!level.isClientSide()) {
        CannonSeatEntity seat = new CannonSeatEntity(ModEntities.CANNON_SEAT.get(), level);
        // 调整坐标匹配大炮模型的骑乘位置
        seat.setPos(pos.getX() + 0.5, pos.getY() + 0.2, pos.getZ() + 0.5);
        level.addFreshEntity(seat);
        player.startRiding(seat);
    }
}

3. 实现大炮模型全域点击判定

由于大炮是BlockEntity,重写方块的use()方法,通过自定义VoxelShape匹配模型轮廓:

@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
    if (!level.isClientSide()) {
        CannonBlockEntity be = (CannonBlockEntity) level.getBlockEntity(pos);
        if (be != null) {
            // 手动构建匹配大炮模型的VoxelShape(可组合多个小立方体模拟外形)
            VoxelShape cannonShape = Shapes.or(
                Shapes.box(0.2, 0, 0.2, 0.8, 0.6, 0.8),
                Shapes.box(0.3, 0.6, 0.3, 0.7, 1.2, 0.7)
            );
            // 转换点击坐标为方块本地坐标并检查是否在模型形状内
            Vec3 localHit = hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ());
            if (cannonShape.bounds().contains(localHit)) {
                triggerRide(player, pos);
                return InteractionResult.SUCCESS;
            }
        }
    }
    return super.use(state, level, pos, player, hand, hit);
}

若GeckoLib模型内置碰撞数据,可直接从模型实例中提取对应VoxelShape替代手动构建。

4. 额外物理特性禁用

CannonSeatEntity构造方法中关闭冗余物理特性:

public CannonSeatEntity(EntityType<? extends CannonSeatEntity> type, Level level) {
    super(type, level);
    this.noPhysics = true;
    this.setInvisible(true);
    this.setInvulnerable(true);
    this.setNoGravity(true);
}

内容的提问来源于stack exchange,提问作者Krysztok

火山引擎 最新活动