Ray Tracing

Basiquement, le tracé laser (ray tracing) est une méthode qui permet de déterminer le chemin d’une particule à travers un système de coordonnées. Dans SpongeAPI, c’est implémenté avec la classe BlockRay pour détecter les blocs dans la trajectoire d’une ligne arbitraire dans l’espace. Il est souvent utilisé pour trouver le bloc que le Player regarde.

Vous pouvez spécifier l’origine du laser en utilisant la méthode BlockRay#from, en passant en argument soit une Location, un Extent et un Vector3d, ou une Entity. La méthode retournera un BlockRay.BlockRayBuilder.

Astuce

Si vous spécifier que l’origine du rayon est une Entity, la direction par défaut sera la direction à laquelle l”Entity pointe.

Pour spécifier le point final d’un laser, vous pouvez utiliser la méthode BlockRay.BlockRayBuilder#to(Vector3d), qui définit la direction et le point final. Alternativement, vous pouvez spécifier une direction en utilisant BlockRay.BlockRayBuilder#direction(Vector3d) et également une limite en utilisant BlockRay.BlockRayBuilder#distanceLimit(double).

Note

La distance par défaut est de 1000 blocs pour éviter une itération infinie. Pour désactiver la limite de blocs, utilisez une valeur négative avec BlockRayBuilder#distanceLimit(double).

Filtrage

Les filtres déterminent quels blocs sont acceptés par le BlockRay. Pour ajouter un filtre, utilisez la méthode BlockRayBuilder#stopFilter ou BlockRayBuilder#skipFilter, en passant un ou plusieurs Predicate<BlockRayHit<E>>s (où E hérite de Extent). Le BlockRayBuilder#stopFilter stoppe le cast quand il atteint un blocc passant dans le filtre, le BlockRayBuilder#skipFilter ignore les blocs passés dans le filtre. Ces filtres peuvent être enchaînés pour créer un BlockRay complexe. Pour plus d’aisance, BlockRay contient les méthodes suivantes pour filtrer la plupart des cas communs:

  • allFilter : retourne un filtre qui accepte tous les blocs
  • onlyAirFilter : retourne un filter qui accepte seulement les blocs d’air
  • blockTypeFilter(BlockType) : rretourne un filtre acceptant uniquement le BlockType spécifié
  • continueAfterFilter(Predicate<BlockRayHit<E>>, int) : retourne un filtre qui continue après le filtre donné du nombre de blocs spécifié.

Évidemment, vous pouvez également écrire vos propres filtres Predicate<BlockRayHit<E>>.

Finalement, utilisez BlockRay.BlockRayBuilder#build() pour terminer la construction du BlockRay. Ci-dessous, vous trouverez un exemple d’utilisation du BlockRayBuilder pour récupérer le premier bloc qui n’est pas de l’air qu’un Player regarde.

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

Nous pouvons réécrire l’exemple ci-dessus en utilisant le BlockRay#skipFilter(Predicate<BlockRayHit>) en plus de BlockRay#stopFilter(Predicate<BlockRayHit>). Cela aura pour effet d’ignorer tous les blocs d’air et s’arrête au premier bloc touché qui n’est pas de l’air.

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

Utilisation du BlockRay

Étant donné que le BlockRay implémente Iterator, vous pouvez utiliser des méthodes comme hasNext et next (mais pas remove) pour itérer à travers les BlockRayHits générés par le BlockRay. En outre, vous pouvez utiliser la méthode BlockRay#reset() pour remettre l’itérateur à sa position de départ. Plutôt qu’itérer à travers les BlockRayHits, vous pouvez également utiliser la méthode BlockRay#end() pour tracer le laser de blocs jusqu’à la fin de son chemin et obtenir le dernier bloc accepté par le filtre (ou aucun si la limite de blocs a été atteinte).

Utilisation du BlockRayHit

BlockRayHit contient les informations à propos de chaque bloc croisé par le laser. Il contient la position du bloc, la direction du laser, les coordonnées de l”intersection entre le laser et le bloc, et d’autres données similaires. Le code suivant utilise le BlockRay de l’exemple précédent pour afficher la position du premier bloc qui n’est pas de l’air en face du joueur.

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