光線追蹤

Generically, ray tracing is a method of determining the path of a particle through a coordinate system. In SpongeAPI, this is implemented with the BlockRay class to discover blocks in the path of an arbitrary line in space. A common use case could be to find the block that a Player is looking at.

You can specify the origin of the ray using the BlockRay#from method, passing in either a Location, an Extent and a Vector3d, or an Entity. The method will return a BlockRay.BlockRayBuilder.

小訣竅

If you specify the origin of the ray to be an Entity, the default direction will be the direction in which the Entity is pointing.

To specify the end point, you can use the BlockRay.BlockRayBuilder#to(Vector3d) method, which will set both the direction and ending location. Alternatively, you can specify a direction using BlockRay.BlockRayBuilder#direction(Vector3d) and also a distance limit using BlockRay.BlockRayBuilder#distanceLimit(double).

備註

The default distance limit is 1000 blocks as a safeguard to prevent infinite iteration. To disable the distance limit, use a negative value with BlockRayBuilder#distanceLimit(double).

過濾

Filters determine what blocks are accepted by the BlockRay. To add a filter, use the BlockRayBuilder#stopFilter or BlockRayBuilder#skipFilter method, passing in one or many Predicate<BlockRayHit<E>>s (where E extends Extent). The BlockRayBuilder#stopFilter stops the ray cast when it hits a block that passes the given filter and the BlockRayBuilder#skipFilter skips all blocks that pass the filter. These filters can be chained together to create a complex BlockRay. For convenience,``BlockRay`` contains the following methods for common filter use cases:

  • allFilter: returns a filter accepting all blocks
  • onlyAirFilter: returns a filter accepting only air
  • blockTypeFilter(BlockType): returns a filter accepting only the specified BlockType
  • continueAfterFilter(Predicate<BlockRayHit<E>>, int): returns a filter that continues past the given filter by the specified number of blocks.

Of course, you can also write your own Predicate<BlockRayHit<E>> filter.

Finally, use BlockRay.BlockRayBuilder#build() to finish building the BlockRay. An example usage of BlockRayBuilder to get the first non-air block a Player is looking at is provided below.

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)
    .stopFilter(BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1)).build();

We can rewrite the above to use BlockRay#skipFilter(Predicate<BlockRayHit>) in addition to BlockRay#stopFilter(Predicate<BlockRayHit>). This will skip all air blocks and stop at the first non-air block it hits.

BlockRay<World> blockRay = BlockRay.from(player)
    .skipFilter(BlockRay.onlyAirFilter()).stopFilter(BlockRay.allFilter()).build();

使用 BlockRay

因為``BlockRay``有內建``Iterator``,你可以使用像是``hasNext`` 或 next``(但是不能用 ``remove )之類的方法去對:javadoc:BlockRayHit 形成迴圈。你也可以用:javadoc:`BlockRay#reset()`之類的方法來重置回預設。你還可以用:javadoc:`BlockRay#end()`之類的方法直接得到最後被接受的方塊(或者,如果方塊限制到達後,被自動選為無)。

使用BlockRayHit

BlockRayHit contains information about each block intersected by the ray. It contains the location of the block, the direction of the ray, the coordinates of the intersection of the ray and the block, and other similar data. The following code uses the BlockRay from the previous example to print out the location of the first non-air block in front of the player.

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());
}