Search code examples
javavectorbukkitspigot

Java Spigot Set the direction of the vector relative to the player's position


I have a problem with the correct vector alignment. I want to get a vector pointing in the same direction as the player, but with a constant Y value of 0. The point is, whatever the player's vertical and horizontal rotation, the vector's Y value was 0. The vector is always supposed to point horizontally (value 0), but keeping the direction of the player's rotation.

This picture shows the situation from the side. The red line represents an example of the player's viewing direction (up - down), and the green one the effect I want to achieve. Regardless of the direction in which the player is looking, up or down, the green line remains unchanged:

Side view

Here, in turn, I have presented this situation from the top. The red line is the player's viewing direction (left - right) and the green is the effect I want to achieve. As you can see, the player's rotation on this axis sets my vector exactly the same.

View from above

I was able to write a piece of code, but it doesn't behave correctly: the Y axis gets higher and higher as the player turns up or down. I don't know why:

Vector playerDirection = player.getLocation().getDirection();

Vector vector = new Vector(playerDirection.getX(), 0, playerDirection.getZ()).normalize().multiply(3);

How to do it correctly?


Solution

  • tl;dr:

    Vector vector = new Vector(-1 * Math.sin(Math.toRadians(player.getLocation().getYaw())), 0, Math.cos(Math.toRadians(player.getLocation().getYaw())));
    

    You are missing a fundamental principal of creating a new Vector based on where a player is looking. I don't know the math of it very well, but I can mess around with the math of people who are better than I at Geometry.

    As such, let's try to reduce the number of Vector variables you have defined. Taking a quick peek at the source for Location, we can actually create your Vector directly to avoid having multiple defined.

    public Vector getDirection() {
        Vector vector = new Vector();
    
        double rotX = this.getYaw();
        double rotY = this.getPitch();
    
        vector.setY(-Math.sin(Math.toRadians(rotY)));
    
        double xz = Math.cos(Math.toRadians(rotY));
    
        vector.setX(-xz * Math.sin(Math.toRadians(rotX)));
        vector.setZ(xz * Math.cos(Math.toRadians(rotX)));
    
        return vector;
    }
    

    As you can see, the pitch and yaw of a player are not a 1:1 relationship. No idea why, but let's repurpose their logic.

    Here's how we'll do that:

    public Vector getVectorForAdixe(Location playerLoc) {
        Vector vector = new Vector();
        
        double rotX = playerLoc.getYaw();
        double rotY = 0; // this is the important change from above
    
        // Original Code:
        // vector.setY(-Math.sin(Math.toRadians(rotY)));
        // Always resolves to 0, so just do that
        vector.setY(0);
    
        // Original Code:
        // double xz = Math.cos(Math.toRadians(rotY));
        // Always resolves to 1, so just do that
        double xz = 1;
    
        vector.setX(-xz * Math.sin(Math.toRadians(rotX)));
        vector.setZ(xz * Math.cos(Math.toRadians(rotX)));
    
        return vector;
    

    Nice! Now, cleaning it up a bit to remove those comments and unnecessary variables:

    public Vector getVectorForAdixe(Location playerLoc) {
        Vector vector = new Vector();
        
        double rotX = playerLoc.getYaw();
    
        vector.setY(0);
    
        vector.setX(-1 * Math.sin(Math.toRadians(rotX)));
        vector.setZ(Math.cos(Math.toRadians(rotX)));
    
        return vector;
    

    Why does this math work like that? No idea! But this should almost certainly work for you. Could even inline it if you really wanted to keep it how you had it originally:

    Vector vector = new Vector(-1 * Math.sin(Math.toRadians(player.getLocation().getYaw())), 0, Math.cos(Math.toRadians(player.getLocation().getYaw())));
    

    Closing note, if you want to be able to get the pitch/yaw FROM the vector, that code is here: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/Location.java#310