BlockRays and Their Use

Hey there!

So I’ve never used BlockRay before. I’m wanting to get a block location x blocks away from the entity based on where they’re looking. I’ve looked through the javadocs and here’s what I’ve come up with:

@Override
protected Location setInitialTarget() {
    BlockRay blockRay = BlockRay.from(getEntity()).build();

    if(blockRay.end().isPresent()){
       return ((BlockRayHit)blockRay.end().get()).getLocation();
    }
    return null;
}

It keeps telling me that ((BlockRayHit)blockRay.end().get()).getLocation(); is returning an empty optional. Does that call only return a value if the last block hit is solid and not air? I need this to be able to work if the block is solid or air.

Thanks!

First of all, you can use distanceLimit() with your aforementioned x variable.
Second, yes, if the Optional is empty, it didn’t hit anything.

Ah okay, that’s what I was afraid of. I wasn’t sure if it’d count air blocks. So I suppose I’ll have to do this calculation myself. I believe I remember seeing some kind of Direction enum of something along those lines that I could use to iterate in the correct direction.

I think there’s a way to set the BlockRay filters such that it accepts air blocks. Optional.empty is only returned if nothing passed the filters, not specifically air.

Oooooh okay. I’ll have a look through those here soon!

Would you happen to know how to use these filters? I see I can do
BlockRay#allFilter()
but that returns a filter, not sets it. I thought these would be used in the BlockRay#Builder, but that only has “continueFilter” and “stopFilter”.

Thanks! :smiley:

Well, you’d put a filter that accepts non-air blocks in the stopFilter, and a filter that accepts any block in the continueFilter.

Okay, so further reading in the javadocs seems to point to filters only being used as a means to skip blocks or to stop the ray. So by default, shouldn’t it accept all blocks if no filters are applied, given that they’re only used as a canceling mechanism?

No, because ‘accepting’ means that it returns a hit; the default behavior is to not ‘hit’ on any air blocks (else you’d have a large number of hits to contend with).

Oh okay. So then I’d want the “skipFilter” to use all blocks if I simply want the ray to end at a certain distance. I’ll give that a shot!

No, because then it won’t record a ‘hit’ at the end either; that was your original problem. The end doesn’t constitute a ‘hit’ if there isn’t a block there.

Well crap. haha. So then I’d probably be better off doing this iteration myself. Perhaps I can somehow get the entity’s direction and use the Direction enum to get the proper offsets to use for iteration

That would only return the directions in the six cardinal directions, though. If you really want to get the exact location, you can use Entity#getTransform().getRotation() and use a little bit of trigonometry to find the vector of the line from their head to 10 blocks away or whatever.

Hmmm. The Direction enum has 3 offsets though (x, y, z respectively). So doing like NORTH.offsets() should return a Vector3D containing (0, 0, -1) I believe. Just need to figure out what Vector3D it takes to get the proper direction. Probably the entity rotation

But you won’t be able to glean anything useful from that information. If I am looking directly north, then Direction.NORTH would be correct. But what if I’m looking diagonally upwards?

Oh, I see what you mean. So yeah, this probably would require me to use some sort of advanced math to figure this out.

Which BlockRay does for you… I remember having a bunch of problems getting the filters just right, but I know it’s possible, so I’d suggest trying to figure out how to set the right filters, rather than do a bunch of math. I wish I had the old code were I had a working BlockRay, but I honestly have no idea where it is.

Edit:
I’d suggest taking a look at the docs:
https://docs.spongepowered.org/master/en/plugin/ray-tracing.html

I think what you want is:

BlockRay<World> blockRay = BlockRay.from(entity)
    .filter(BlockRay.continueAfterFilter(BlockRay.allFilter(), 1))
    .blockLimit(x)
    .build();

Or possibly:

BlockRay<World> blockRay = BlockRay.from(entity)
    .filter(BlockRay.continueAfterFilter(h -> false, x))
    .build();
1 Like