Trazado de Rayos (Ray Tracing)

Genéricamente, el trazado de rayos es un método para determinar el camino de una partícula a través de un sistema de coordenadas. En SpongeAPI, esto se implementa con la clase :javadoc: BlockRay para descubrir bloques en la ruta de una línea arbitraria en el espacio. Un caso de uso común podría ser encontrar el bloque que está mirando a :javadoc: Player.

Puede especificar el origen del rayo utilizando el método BlockRay # from, pasando ya sea a :javadoc: Location, una :javadoc:`Extensión` y Vector3d, o una :javadoc:`Entidad`. El método devolverá a BlockRay.BlockRayBuilder.

Truco

Si especifica que el origen del rayo es una Entidad, la dirección predeterminada será la dirección a la que apunta Entidad.

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).

Nota

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).

Filtración

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: devuelve un filtro que acepta todos los bloques
  • onlyAirFilter: devuelve un filtro que acepta solo aire
  • blockTypeFilter(BlockType): devuelve un filtro que acepta solo lo especificado :javadoc: BlockType
  • continueAfterFilter(Predicate<BlockRayHit<E >>, int): devuelve un filtro que continúa más allá del filtro dado por el número especificado de bloques.

Por supuesto, también puede escribir su propio filtro Predicate<BlockRayHit<E>>.

Finalmente, use BlockRay.BlockRayBuilder#build() para terminar de construir el BlockRay. Un ejemplo de uso de BlockRayBuilder para obtener el primer bloque no aéreo que se ve en Player se proporciona a continuación.

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

Usando BlockRay

Como BlockRay implementa Iterator, puedes usar métodos como hasNext y next (pero no remove) para iterar a través de BlockRayHits generados por el BlockRay. Además, puede usar el método BlockRay#reset() para restablecer el iterador a la ubicación de inicio. En lugar de iterar a través del BlockRayHits, también puede usar el método BlockRay#end() para rastrear el bloque de rayos hasta el final de su ruta para obtener el último bloque aceptado por el filtro (o ninguno si se alcanzó el límite de bloque).

Usando BlockRayHit

BlockRayHit contiene información sobre cada bloque intersectado por el rayo. Contiene la ubicación del bloque, la dirección del rayo, las coordenadas de la intersección del rayo y el bloque, y otros datos similares. El siguiente código usa BlockRay del ejemplo anterior para imprimir la ubicación del primer bloque no aéreo delante del jugador.

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