光线追踪

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

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

小技巧

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

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

注解

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

筛选器

BlockRay 可接受方块种类过滤器。要添加过滤器,需要使用 BlockRayBuilder#stopFilterBlockRayBuilder#skipFilter 方法,并向其中传入一个或多个 Preciate<BlockRayHit<E extends Extent>>。其中,BlockRayBuilder#stopFilter 会令过滤器过滤到指定条件时继续光线追踪,而 BlockRatBuilder#skipFilter 则会在遇到符合过滤条件的方块时忽略这些方块。将各种过滤器组合在一起可构成一个复杂的 BlockRay 对象。为简便期间,BlockRay 包含了下列常见的过滤器相关方法:

  • allFilter :所有方块都可以通过筛选

  • onlyAirFilter :只有空气方块可以通过筛选

  • blockTypeFilter(BlockType) :只有给定的 BlockType 可以通过筛选

  • 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)
    .skipFilter(BlockRay.onlyAirFilter()).stopFilter(BlockRay.onlyAirFilter()).build();

我们可以将上面的代码用 BlockRay#skipFilter(Predicate<BlockRayHit>)BlockRay#stopFilter(Predicate<BlockRayHit>) 的组合重写。这段代码将会跳过所有空气方块并在首次遇到非空气方块时停止。

BlockRay<World> blockRay = BlockRay.from(player)
    .skipFilter(BlockRay.onlyAirFilter()).stopFilter(BlockRay.allFilter()).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());
}