Random location within a radius (Not a square area)

Discussion in 'Plugin Development' started by the_merciless, Feb 24, 2013.

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

    the_merciless

    I want to make it so when a player dies they spawn in a random location within a certain radius. I have found ways to get random ints but from what i have found i can only get random locations within a square/rectangular area. Any ideas?
     
  2. Offline

    gamerzap

    You could try generating a random x, a random y and a random z and then check if the difference between x, y, and z from the center squared added together are equal to the radius squared, but I might have my math wrong...
     
  3. Set a min and max number for x, y, z and generate random number imbetween them then create a new Location.
     
  4. Offline

    the_merciless

    Thats a square again!
     
  5. the_merciless
    The best way to do it would be to use a random generator to generate coordinates within a rectangular area, and just re-run the check if the distance is greater than some distance you specify. This way the areas on the corners of the area will not be valid.
     
  6. Offline

    gamerzap

    That's what I said... (But I might have gotten the distance incorrectly)
     
  7. Offline

    the_merciless

    Im using a square area at the moment just to test my code but im getting an error with negative numbers,

    Code:
    2013-02-24 20:12:52 [SEVERE] Could not pass event PlayerRespawnEvent to MercilessPvp v1.0.0[1.4.7]
    org.bukkit.event.EventException
        at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:427)
        at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62)
        at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:477)
        at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:462)
        at net.minecraft.server.v1_4_R1.PlayerList.moveToWorld(PlayerList.java:368)
        at net.minecraft.server.v1_4_R1.PlayerList.moveToWorld(PlayerList.java:325)
        at net.minecraft.server.v1_4_R1.PlayerConnection.a(PlayerConnection.java:1147)
        at net.minecraft.server.v1_4_R1.Packet205ClientCommand.handle(SourceFile:30)
        at net.minecraft.server.v1_4_R1.NetworkManager.b(NetworkManager.java:290)
        at net.minecraft.server.v1_4_R1.PlayerConnection.d(PlayerConnection.java:113)
        at net.minecraft.server.v1_4_R1.ServerConnection.b(SourceFile:39)
        at net.minecraft.server.v1_4_R1.DedicatedServerConnection.b(SourceFile:30)
        at net.minecraft.server.v1_4_R1.MinecraftServer.r(MinecraftServer.java:598)
        at net.minecraft.server.v1_4_R1.DedicatedServer.r(DedicatedServer.java:224)
        at net.minecraft.server.v1_4_R1.MinecraftServer.q(MinecraftServer.java:494)
        at net.minecraft.server.v1_4_R1.MinecraftServer.run(MinecraftServer.java:427)
        at net.minecraft.server.v1_4_R1.ThreadServerApplication.run(SourceFile:849)
    Caused by: java.lang.IllegalArgumentException: n must be positive
        at java.util.Random.nextInt(Unknown Source)
        at me.merci.MercilessPvp.Pvplistener.getRandom(Pvplistener.java:88)
        at me.merci.MercilessPvp.Pvplistener.onrespawn(Pvplistener.java:76)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:425)
        ... 16 more
    Why am i getting this?
     
  8. Offline

    gamerzap

    Are you trying to use a negative number as an argument in Random.nextInt?
     
  9. Offline

    the_merciless

    Yes, can i not do that. If so how do i get around it. Heres my code so far:

    Code:
    @EventHandler (priority = EventPriority.HIGHEST)
        public void onrespawn(PlayerRespawnEvent e ){
            Player p = e.getPlayer();
            String pname = p.getName();
            int ranx = getRandom(-678,-878);
            int ranz = getRandom(-428,-628);
            Location ranl = new Location(Bukkit.getServer().getWorld("merci"), ranx, 64, ranz);
            p.teleport(ranl);     
            ItemStack[] inv = inventories.get(pname);
            ItemStack[] arm = armour.get(pname);
            p.getInventory().setContents(inv);
            p.getInventory().setArmorContents(arm);
        }
     
        public int getRandom(int lower, int upper) {
            Random random = new Random();
            return random.nextInt((upper - lower) + 1) + lower;
        }
     
  10. Offline

    InflamedSebi

    a math cycle is defined like: r² = x² + y²
    (because minecrafts y is the hight a cycle on the ground is: r² = x² + z²)
    so:
    each pair of numbers who fit that, are part of the cycle
    next step is to get a random position on the edge of a cycle for a constant r (distance from the center):
    so create a new random generator

    Random rndGen = new Random();

    and get a random number ... this is ur x coordinate.

    int x = rndGen.nextInt(r);

    now u can calc the z = (r² - x²)

    int z = Math.sqrt(Math.pow(r,2) - Math.pow(x,2));

    now u got a random position on the edge of a cycle with the radius r

    because u dont want ur player alway spawn on the border of the cycle, u need another random for the radius, lower than the maximum distance:

    int r = rndGen.nextInt(maxDistance);


    put all thogether:
    Code:java
    1. Random rndGen = new Random();
    2. int r = rndGen.nextInt(maxDistance);
    3. int x = rndGen.nextInt(r);
    4. int z = Math.sqrt(Math.pow(r,2) - Math.pow(x,2));


    now u may realize that x and z always are positive, but they should also be able to get negative values. An easy fix is to add:

    if(rndGen.nextBoolean()) x *= -1;
    if(rndGen.nextBoolean()) z *= -1;


    the completed code:
    Code:java
    1. Random rndGen = new Random();
    2. int r = rndGen.nextInt(maxDistance);
    3. int x = rndGen.nextInt(r);
    4. int z = Math.sqrt(Math.pow(r,2) - Math.pow(x,2));
    5. if(rndGen.nextBoolean()) x *= -1;
    6. if(rndGen.nextBoolean()) z *= -1;
    7.  


    thats all :D

    x and z are somewhere in a cycle with the radius "maxDistance"

    :D

    now get the players position oldPx, oldPy, oldPz

    and add the calculated values...

    newPx = oldPx + x;
    newPz = oldPz + z;

    and make sure they dont spawn in solid blocks xD
     
  11. Offline

    Technius

    I know a really random way to do it. Generate a double using Random.nextDouble, multiply it by the diameter, and then subtract it by the radius, and then add it to the center of the circle. Randomizing the y is probably not a good idea as the players could possibly spawn into a wall. Grabbing the height of the highest block at the randomized x and z coordinates would be a nice y.

    Here's an example if you have no idea what to do:
    Code:
    double diameter = radius*2;
    Location center = //Center of the circle
    Random rand = new Random();
    double x = center.getX() + (rand.nextDouble()*diameter - radius);
    double z = center.getY() + (rand.nextDouble()*diameter - radius);
    Location spawn = new Location(world, x, y, z);
    
    Edit: This is a square. Scroll down to my post below for a circle.
     
  12. Offline

    the_merciless

    Will this work with negatives?
     
  13. Offline

    Technius

    It will work with negative coordinates. The coordinates of the center of the circle square is added to the randomly generated coordinates.

    Random.nextDouble() generates a random double from 0.o to 1.0, which is pretty nice.
     
  14. Offline

    InflamedSebi


    this is not a cycle ...

    f.e.(lets say "rand.nextDouble()" is generating 1 both times and we want a radius of 10 blocks):

    diameter = 10 * 2 = 20
    center = 0, 0, 0
    x = 0 + (1*20 - 10) = 10
    z = 0 + (1*20 - 10) = 10
    spawn = 10, 0, 10

    to make a cycle check:
    r² = x² + z²
    10² = 10² + 10²
    100 = 200 -> false , not part of the cycle

    also ur solution always produces a positive value

    here i finished a correct solution

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

    desht

    Code:
    // given loc as the centre of the area you want, r as the max radius...
    Random rnd = new Random();
    double a = rnd.nextDouble() * 2 * Math.PI;
    double dist = rnd.nextDouble() * r;
    Location wanted = loc.clone().add(dist * Math.sin(a), 0, dist * Math.cos(a));
    
    If the terrain is isn't flat,you might also want to use World.getHighestBlockYAt() to ensure the location is on the surface.
     
  16. Offline

    Technius

    Do you mean a circle?

    You're right, it is a square. I'll make it a circle by using trig.

    Code:
    Location center = // Center of the circle
    Random rand = new Random();
    double angle = rand.nextDouble()*360; //Generate a random angle
    double x = center.getX() + (rand.nextDouble()*radius*Math.cos(Math.toRadians(angle))); // x
    double z = center.getZ() + (rand.nextDouble()*radius*Math.sin(Math.toRadians(angle))); // z
    Location newloc = new Location(world, x, y, z);
    
    All theory here and it's untested.

    Edit: desht ninja'd me!
    Edit 2: desht's solution has the same x and z offsets though...

    Actually, it can produce a negative value.

    center = {5,5,0}
    radius = 5
    genx = 0.3
    genz = 0.0
    x = 5 + (3 - 5) = 2
    z = 0 + (0 - 5) = -5

    But it's a square.
     
  17. Offline

    InflamedSebi

    oh yeah circle ... nah english is not my mother tongue xD
     
Thread Status:
Not open for further replies.

Share This Page