光线追踪

通常说来,光线追踪的用处是确定粒子通过指定坐标系的路径。在 SpongeAPI 中,解决问题的是 BlockRay 类,该类的作用就是发现空间中任意路径经历的方块。一个常见的用法可能是找到一个 Player 正在观察的方块。

你可以通过使用 BlockRay#from 方法,并传入一个 Location 、一个 Extent 和一个 Vector3d ,或者只传入一个 Entity ,来指定光线的起始点。该方法将返回一个 BlockRay.BlockRayBuilder

小技巧

如果你指定光线追踪的原点为 Entity ,那么默认方向将是 Entity 所指向的方向。

如果你想要指定终点,你可以通过使用 BlockRay.BlockRayBuilder#to(Vector3d) 方法。该方法将同时设置方向和终点。此外,你也可以使用 BlockRay.BlockRayBuilder#direction(Vector3d) 方法指定一个方向,然后通过 BlockRay.BlockRayBuilder#blockLimit(int) 方法指定方块数量的限制。

注解

默认的方块数量限制为 1000 个方块,这个数字已经接近无穷了,它的存在意义只是作为防止无限迭代的保护措施。如要禁用数量方块限制,你可以将一个负数传入 BlockRayBuilder#blockLimit(int) 方法。

筛选器

筛选器用于指定哪些方块会被 BlockRay 追踪到。通过使用 BlockRayBuilder#filter 方法,并传入一个或若干 Predicate<BlockRayHit<E>> (泛型 EExtent 的子类),你可以添加一个筛选器。为方便起见, BlockRay 类提供了一些方法用于常见的筛选器类型。

  • allFilter :所有方块都可以通过筛选
  • onlyAirFilter :只有空气方块可以通过筛选
  • blockTypeFilter(BlockType) :只有给定的 BlockType 可以通过筛选
  • maxDistanceFilter(Vector3d, double) :只有距离给定 Vector3d 的给定最大距离之内的方块可以通过筛选
  • continueAfterFilter(Predicate<BlockRayHit<E>>, int) :不仅通过指定筛选器的方块可以通过筛选,在第一个不满足指定筛选器的方块出现后的指定数量的所有方块都可以通过筛选。

当然,你也可以写你自己的 Predicate<BlockRayHit<E>> 筛选器。

最后,调用 BlockRay.BlockRayBuilder#build() 方法生成一个 BlockRay 。下面是一个使用 BlockRayBuilder 以试图追踪玩家查看的第一个非空气方块的示例:

import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.util.blockray.BlockRay;
import org.spongepowered.api.world.World;

Player player;
BlockRay<World> blockRay = BlockRay.from(player)
    .filter(BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1)).build();

使用 BlockRay

因为 BlockRay 实现了 Iterator ,所以你可以使用诸如 hasNext 或者 next (不过不能使用 remove )这样的方法去迭代其生成的 BlockRayHit 。此外,你可以使用 BlockRay#reset() 方法使其回到原位。除了进行普通的迭代外,你还可以通过使用 BlockRay#end() 方法直接获取最后一个可用的方块(如果达到了方块数量限制,那么将返回空)。

使用 BlockRayHit

BlockRayHit 包含由光线相交的每个块的信息。它包含块的位置,光线的方向,光线和块的 交点 的坐标,以及其他类似的数据。下面的代码使用前面例子中的 BlockRay 来打印出玩家前面的第一个非空气块的位置。

import org.spongepowered.api.util.blockray.BlockRayHit;
import java.util.Optional;

BlockRay<World> blockRay;
Optional<BlockRayHit<World>> hitOpt = blockRay.end();
if (hitOpt.isPresent()) {
    BlockRayHit<World> hit = hitOpt.get();
    System.out.println("Found " + hit.getLocation().getBlockType() + " block at "
        + hit.getLocation() + " with intersection at " + hit.getPosition());
}