Looking if player is in radius of certain location

Discussion in 'Plugin Development' started by Infernus, Mar 16, 2011.

Thread Status:
Not open for further replies.
  1. Offline

    Infernus

    Let's say I have a location here, now I want to check if this player is in a radius of for example 4 blocks of the spawn. How would I do this?

    I would really appreciate it VERY much to know this.
     
  2. Offline

    zweizeichen

    Vectors...
     
  3. Offline

    Infernus


    That's a whole bunch of information.. Could you please be more clear?
     
  4. Offline

    zweizeichen

    Bukkit has a class for working with vectors.
    So you can measure the distance between the player and the position (spawn or something).
     
  5. Offline

    DiddiZ

    Code:
    private boolean isPlayerWithinRadius(Player player, Location loc, double radius)  {
        int x = loc.getX() - player.getLocation().getX();
        int y = loc.getY() - player.getLocation().getY();
        int z = loc.getZ() - player.getLocation().getZ();
        double distance = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2);
        if (distance <= radius)
            return true;
        else
            return false;
    }
    This is the way MeasuringTape works. I extended it a bit for clearness reasons, it can be merged to a single line.
     
  6. Offline

    Infernus

    Just what I needed! Thanks very much! :)
     
  7. Offline

    eisental

    I think this is a pretty inefficient way to do it if you don't need to know the exact distance.
    This is how it's done using the Vector class:
    Code:
        private boolean isPlayerWithinRadius(Player player, Vector v, double radius) {
            return player.getLocation().toVector().isInSphere(v, radius);
        }
    
    Code:
    ...
        Vector origin = originLocation.toVector();
        if (isPlayerWithinRadius(player, origin, radius);
    ...
    
    If you can permanently store the origin location as a vector it would be even more efficient.
     
  8. Offline

    DiddiZ

    The isInSpehere method works exactly the same way.
    To assign the distance to a variable is only for clearness reasons, as I said.
    The only difference is, that you have to cast into a vector.
     
  9. Offline

    eisental

    isInSphere doesn't use Math.sqrt(). That's what makes it more efficient.
    Actually you don't cast the Location into a Vector. The call to Location.toVector() returns a new Vector object each time. I'm not sure how much effect object creation has on actual performance in Java.
     
  10. Offline

    DiddiZ

    Interesting. I'll have to check the time difference bet sqrt and pow.
     
  11. Offline

    eisental

    pow is just a multiplication. sqrt is much more complex to calculate than pow so it will be slower. I don't really know whether it's significant or not (depends on your cpu etc.).
     
  12. Offline

    DiddiZ

    I tested it:
    Code:
    public class SpeedTest
    {
        public static void main (String argv[]) {
            int zahl1 = 10, zahl2 = 100;
            long start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                Math.sqrt(zahl2);
            }
            System.out.println("Sqrt Took: " + (System.currentTimeMillis() - start) + "ms");
            start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                Math.pow(zahl1, 2);
            }
            System.out.println("Pow Took: " + (System.currentTimeMillis() - start) + "ms");
        }
    }
    Surprisingly, sqrt seems to run about 80 times faster.
    Sqrt Took: 47ms
    Pow Took: 3750ms

    Also in a real implementation it make a difference:
    Code:
    import org.bukkit.Location;
    
    public class SpeedTest
    {
        public static void main (String argv[]) {
            Location loc1 = new Location(null, -846, 72, 50);
            Location loc2 = new Location(null, 500, 43, -287);
            double radius = 20;
            long start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                isPlayerWithinRadiusSqrt(loc1, loc2, radius);
            }
            System.out.println("Sqrt Took: " + (System.currentTimeMillis() - start) + "ms");
            start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                isPlayerWithinRadiusPow(loc1, loc2, radius);
            }
            System.out.println("Pow Took: " + (System.currentTimeMillis() - start) + "ms");
            start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                loc1.toVector().isInSphere(loc2.toVector(), radius);
            }
            System.out.println("Vector Took: " + (System.currentTimeMillis() - start) + "ms");
            start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                vec2.isInSphere(vec2, radius);
            }
            System.out.println("Precasted Vector Took: " + (System.currentTimeMillis() - start) + "ms");
        }
    
        private static boolean isPlayerWithinRadiusSqrt(Location loc1, Location loc2, double radius)  {
            return  Math.sqrt(Math.pow(loc1.getX() - loc2.getX(), 2) + Math.pow(loc1.getY() - loc2.getY(), 2) + Math.pow(loc1.getZ() - loc2.getZ(), 2)) <= radius;
        }
    
        private static boolean isPlayerWithinRadiusPow(Location loc1, Location loc2, double radius)  {
            return  Math.pow(loc1.getX() - loc2.getX(), 2) + Math.pow(loc1.getY() - loc2.getY(), 2) + Math.pow(loc1.getZ() - loc2.getZ(), 2) <= Math.pow(radius, 2);
        }
    }
    
    Sqrt Took: 11734ms
    Pow Took: 13875ms
    Vector Took: 37235ms
    Precasted Vector Took: 15735ms

    I'm a speed freak :D
    But the difference is only about 0.2 ns per run, so it might be insignificant.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 12, 2016
  13. Offline

    eisental

    That IS surprising :p
    I get something similar.

    Running this:
    Code:
    public class SpeedTest
    {
        public static void main (String argv[]) {
            int zahl1 = 10, zahl2 = 100;
            double res;
    
            long start = System.nanoTime();
            for (int i = 0; i < 1000000000; i++) {
                res = Math.sqrt(zahl2);
            }
            System.out.println("Sqrt Took: " + (System.nanoTime() - start)/10000000d + "ms");
    
            start = System.nanoTime();
            for (int i = 0; i < 1000000000; i++) {
                res = Math.pow(zahl1, 2);
            }
            System.out.println("Pow Took: " + (System.nanoTime() - start)/10000000d + "ms");
    
            start = System.nanoTime();
            for (int i = 0; i < 1000000000; i++) {
                res = zahl1*zahl1;
            }
            System.out.println("Pow multiplication took: " + (System.nanoTime() - start)/10000000d + "ms");
    
            start = System.nanoTime();
            for (int i = 0; i < 1000000000; i++) {
                res = Math.sqrt(zahl2);
            }
            System.out.println("Sqrt Took: " + (System.nanoTime() - start)/10000000d + "ms");
        }
    }
    
    I get:
    Code:
    eisental:SpeedTest eisental$ java -jar dist/SpeedTest.jar
    Sqrt Took: 0.1717ms
    Pow Took: 1045.2146ms
    Pow multiplication took: 0.0039ms
    Sqrt Took: 0.0029ms
    eisental:SpeedTest eisental$ java -jar dist/SpeedTest.jar
    Sqrt Took: 0.1637ms
    Pow Took: 1047.0194ms
    Pow multiplication took: 0.0026ms
    Sqrt Took: 0.0029ms
    eisental:SpeedTest eisental$ java -jar dist/SpeedTest.jar
    Sqrt Took: 0.173ms
    Pow Took: 1062.1983ms
    Pow multiplication took: 0.0030ms
    Sqrt Took: 0.0027ms
    eisental:SpeedTest eisental$ java -jar dist/SpeedTest.jar
    Sqrt Took: 0.2903ms
    Pow Took: 1079.8242ms
    Pow multiplication took: 0.0035ms
    Sqrt Took: 0.0027ms
    
    First calculation is always slower and the Math.pow function is SO much slower then doing zahl1*zahl1. Maybe this isn't the best way to test it in a multi-thread environment.

    EDIT: So the best would probably be:
    Code:
    private boolean isPlayerWithinRadius(Player player, Location loc, double radius)  {
        int x = loc.getX() - player.getLocation().getX();
        int y = loc.getY() - player.getLocation().getY();
        int z = loc.getZ() - player.getLocation().getZ();
        int distance = x*x + y*y + z*z;
    
        return (distance <= radius*radius)
    }
    
    No new objects and no Math.pow() or Math.sqrt()
     
  14. Offline

    DiddiZ

    Yes, tested this:
    Code:
    import org.bukkit.Location;
    
    public class SpeedTest
    {
        public static void main (String argv[]) {
            Location loc1 = new Location(null, -846, 72, 50);
            Location loc2 = new Location(null, 500, 43, -287);
            double radius = 20;
            long start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                isPlayerWithinRadiusSqrt(loc1, loc2, radius);
            }
            System.out.println("Sqrt Took: " + (System.currentTimeMillis() - start) + "ms");
            start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                isPlayerWithinRadiusMulti(loc1, loc2, radius);
            }
            System.out.println("Multi Took: " + (System.currentTimeMillis() - start) + "ms");
            start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                isPlayerWithinRadiusMultiAll(loc1, loc2, radius);
            }
            System.out.println("MultiAll Took: " + (System.currentTimeMillis() - start) + "ms");
        }
    
        private static boolean isPlayerWithinRadiusSqrt(Location loc1, Location loc2, double radius)  {
            return  Math.sqrt(Math.pow(loc1.getX() - loc2.getX(), 2) + Math.pow(loc1.getY() - loc2.getY(), 2) + Math.pow(loc1.getZ() - loc2.getZ(), 2)) <= radius;
        }
    
        private static boolean isPlayerWithinRadiusMulti(Location loc1, Location loc2, double radius)  {
            return  Math.pow(loc1.getX() - loc2.getX(), 2) + Math.pow(loc1.getY() - loc2.getY(), 2) + Math.pow(loc1.getZ() - loc2.getZ(), 2) <= radius*radius;
        }
    
        private static boolean isPlayerWithinRadiusMultiAll(Location loc1, Location loc2, double radius)  {
            return (loc1.getX() - loc2.getX())*(loc1.getX() - loc2.getX()) + (loc1.getY() - loc2.getY())*(loc1.getY() - loc2.getY()) + (loc1.getZ() - loc2.getZ())*(loc1.getZ() - loc2.getZ()) <= radius*radius;
        }
    }
    Sqrt Took: 11656ms
    Multi Took: 11094ms
    MultiAll Took: 844ms

    It's way faster. About 44 times than vectors and 13 times than my method.
    Thats surprising.
    But the slowest is, when you have tp cast both location into a vector.
    I'll have to update MeasuringTape then :D
     
  15. Offline

    eisental

    Nice, I'll have to make some changes too :oops:.
    I can't believe Java can't replace Math.pow(x,2) to x*x on its own...
     
  16. Offline

    DiddiZ

    To sum this up:
    It's adiseable to use number*number insteed of Math.pow(number, 2), since it's at least 35 times faster.
     
Thread Status:
Not open for further replies.

Share This Page