A Ray in a Minecraft Mod

I want to shoot a ray. And not just parallel to one of the axis of the cartesion coordinate system. I want to look in a direction and shoot a ray in that direction. I want to be able to shoot aray in any direction and walk on it. Like certain ice based superheros. And now I can do that.

In math a line used to be defined as “The [straight or curved] line is the first species of quantity, which has only one dimension, namely length, without any width nor depth, and is nothing else than the flow or run of the point which […] will leave from its imaginary moving some vestige in length, exempt of any width. […] The straight line is that which is equally extended between its points.” While that definition has since changed, it works for Minecraft. A Ray, then, is a half a line: fine a point in the middle, and going only in one direction.

My rays are not infinite, due to resource constraints. So, really, I want to draw an arbitrary line segment, starting from Steve, and continuing on in the direction that Steve is looking.

When I first learned Cartesian coordinates, I learned to plot Y in terms of X. For three dimensions, we need to add in Z. When adding in Z, it might be tempting to plot Z in terms of X as well. But this does not abstract itself to pointing in any arbitrary direction. It turns out it is easier to define X, Y, and Z all in relationship to the distance of the point from the Origin. From our starting point, we move one block along the line, and figure out what the X, Y, and Z values are for that block. Then continue with the next block and so on.

We are going to make our ray shoot 100 blocks.

    double distance = 100.0;

Our origin is the the x,y, and z of the player. From this we need to calculate the end position.

Minecraft gives the facing of the player in two values: Yaw and Pitch. These are pilot terms.

Yaw means roughly “how far around”
Pitch means the angle above or below the horizon.

    double yaw = ( player.rotationYaw + 90 ) * ( Math.PI / 180 );
    double pitch = ( player.rotationPitch * -1 ) * ( Math.PI / 180 );

    double endX = player.posX + distance * Math.cos( yaw ) * Math.cos( pitch );
    double endZ = player.posZ + distance * Math.sin( yaw ) * Math.cos( pitch );
    double endY = player.posY + distance * Math.sin( pitch );

One way to think of this is: go around the circle yaw amount, and then up pitch. As you raise the pitch, the X and Z values get closer to the origin, while the Y value climbs higher into the sky.

Once we have our start and endpoints, we can create an iterator that, when we call next(), will give the next pos object:

Iterator< BlockPos > itr = new LinearIterator(
            player.getPosition( ),
            new BlockPos( endX, endY, endZ ) );

What does this iterator do? Calculates a difference for X, Y, and Z divided by the number of blocks (100 as passed in from before). Deltas,. if you will in the calculus meaning of the term, but with a granularity of one block.
The use the distance formula to figure out the distance from the origin to the current block, and multiply the delta for each by that distance.

    
    public LinearIterator(BlockPos start, BlockPos end) {
        if (start.equals(end)) {
            throw new IllegalArgumentException(
                    "line cannot be defined by two identical points.");
        }
        
        x = start.getX();
        y = start.getY();
        z = start.getZ();
        
        int dx = end.getX() - start.getX();
        int dy = end.getY() - start.getY();
        int dz = end.getZ() - start.getZ();
        int interim = dx * dx + dy * dy + dz * dz;
        this.segmentLength = Math.sqrt(interim);
        
        quantx = dx / segmentLength;
        quanty = dy / segmentLength;
        quantz = dz / segmentLength;
      
    }

If we hit something solid, leave it a lone. Otherwise, put a leaf block at that position (leafs clean up on their own, kind of like how ice is supposed to melt. But Ice does not melt in Minecraft.

    
Iterator< BlockPos > itr = new LinearIterator(
            player.getPosition( ),
            new BlockPos( endX, endY, endZ ) );
    BlockPos previous = null;
    BlockPos current = null;
    BlockPos stairPos = null;
    while ( itr.hasNext( ) ) {
      current = itr.next( );
      Block currentBlock = world.getBlockState( current ).getBlock( );
      if ( currentBlock.equals( Blocks.AIR ) ||
              currentBlock.equals( Blocks.WATER ) ||
              currentBlock.equals( Blocks.PACKED_ICE ) ) {
        world.setBlockState( current, baseState );
        stairPos = current.add( 0, 1, 0 );
      }
      previous = current;
    }

I really need to get an image in there for the ray gun, but for now it is the default pink and black box.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.